您将如何使用 rspec 进行测试?
class SomeClass
def map_url(size)
GoogleMap.new(point: model.location.point, size: size).map_url
end
end
您的测试似乎“非常耦合且难以模拟”这一事实表明代码本身一次做了太多事情。
为了突出这个问题,看看这个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
请注意:
map_url
在新启动的地图上被调用,但不是以正确参数启动的地图。map_url
没有被返回。我认为问题在于您构建代码的方式使其看起来比实际更简单。结果,您的测试过于简单,因此无法完全覆盖该方法的行为。
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 个规范而不是两个,但我认为它更清晰、更清晰地分离了代码中涉及的步骤,并完全涵盖了方法行为。让我知道这是否有意义。
所以我就是这样做的,像这样模拟它感觉非常耦合和脆弱。建议?
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