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返されません。

問題は、コードを構造化した方法によって、実際よりも単純に見えることだと思います。その結果、テストが単純すぎて、メソッドの動作を完全にカバーするには不十分です。

デビッドチェリムスキーからのこのコメントはここに関連しているようです:

TDDには古いガイドラインがあり、テストが傷ついた場合は通常、設計上の問題があるため、テストに耳を傾ける必要があることを示唆しています。テストはテスト対象のコードのクライアントであり、テストが問題になる場合は、コードベース内の他のすべてのクライアントも同様です。このようなショートカットは、すぐに貧弱なデザインの言い訳になります。これをするのは痛いはずなので、私はそれを痛みを伴うままにしておきたい 。

このアドバイスに従って、懸念事項を分離するために、最初にコードを2つの別々のメソッドに分割することをお勧めします。

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

結果のテストコードは長くなり、2つではなく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 に答える