如何使用rspec测试委托给另一个类的启动的方法?

时间:2012-11-30 13:09:55

标签: ruby rspec

您将如何使用rspec进行测试?

class SomeClass
  def map_url(size)
    GoogleMap.new(point: model.location.point, size: size).map_url
  end
end

2 个答案:

答案 0 :(得分:3)

你的测试似乎“非常耦合且易于模拟”的事实表明代码本身一次做了太多事情。

要突出显示这个问题,请查看map_url的这个实现,这是无意义的(对于任何大小的输入都返回“foo”),然后通过测试:

class SomeClass
  def map_url(size)
    GoogleMap.new.map_url
    GoogleMap.new(point: model.location.point, size: size)
    return "foo"
  end
end

请注意:

  1. 正在使用正确的参数启动新映射,但不会对返回值做出贡献。
  2. 正在新发起的地图上调用
  3. map_url,而不是使用正确的参数发起的地图。
  4. map_url的结果未被退回。
  5. 我认为问题在于您构建代码的方式使其看起来比实际更简单。因此,您的测试过于简单,因此无法完全覆盖方法的行为。

    comment from David Chelimsky似乎与此相关:

      

    TDD中有一条旧指南建议你应该听   你的测试,因为当他们受伤时通常会出现设计问题。   测试是被测代码的客户端,如果测试受到伤害,那么   完成代码库中的所有其他客户端。像这样的快捷方式很快   成为糟糕设计的借口。我希望它因为它而保持痛苦   应该伤害来做到这一点。

    遵循这个建议,我建议首先将代码分成两个单独的方法,以隔离问题:

    class SomeClass
      def new_map(size)
        GoogleMap.new(point: model.location.point, size: size)
      end
    
      def map_url(size)
        new_map(size).map_url
      end
    end
    

    然后你可以单独测试它们:

    describe SomeClass do
      let(:some_class) { SomeClass.new }
      let(:mock_map) { double('map') }
    
      describe "#new_map" do
        it "returns a GoogleMap with the correct point and size" do
          map = some_class.new_map('300x600')
          map.point.should == [1,2]
          map.size.should == '300x600'
        end
      end
    
      describe "#map_url" do
        before do
          some_class.should_receive(:new_map).with('300x600').and_return(mock_map)
        end          
    
        it "initiates a new map of the right size and call map_url on it" do
          mock_map.should_receive(:map_url)
          some_class.map_url('300x600')
        end
    
        it "returns the url" do
          mock_map.stub(map_url: "http://www.example.com")
          some_class.map_url('300x600').should == "http://www.example.com"
        end
      end
    end
    

    生成的测试代码更长,有3个规格而不是2个,但我认为它更清晰,更清晰地分离了代码中涉及的步骤,并完全涵盖了方法行为。如果这是有道理的,请告诉我。

答案 1 :(得分:0)

所以这就是我做到的,感觉非常紧密和脆弱,就像这样嘲笑它。建议?

describe SomeClass do
  let(:some_class) { SomeClass.new }

  describe "#map_url" do
    it "should instantiate a GoogleMap with the correct args" do
      GoogleMap.should_receive(:new).with(point: [1,2], size: '300x600') { stub(map_url: nil) }
      some_class.map_url('300x600')
    end

    it "should call map_url on GoogleMap instance" do
      GoogleMap.any_instance.should_receive(:map_url)
      some_class.map_url('300x600')
    end
  end
end