0

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

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

2 回答 2

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. map_url在新启动的地图上被调用,但不是以正确参数启动的地图。
  3. 的结果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 个规范而不是两个,但我认为它更清晰、更清晰地分离了代码中涉及的步骤,并完全涵盖了方法行为。让我知道这是否有意义。

于 2012-11-30T16:35:04.607 回答
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
于 2012-11-30T14:30:52.777 回答