16

これが私のrspecテストです:

it "can release an idea" do
  james.claim(si_title)
  james.release(si_title)
  james.ideas.size.should eq 0
  si_title.status.should eq "available"
end

最後の2should行は本当に悪い考えですか?ブロックごとに1つだけテストする必要があるとどこかで読んだのですがit、タイトルのステータスが変わることを確認するためだけにテスト全体を行うのはばかげているようです(同じ関数が私のコードで両方のことを行います)。

4

4 に答える 4

23

これについての私の解釈は、仕様ごとに正確に1つのアサーション/呼び出しが必要であるというほどではありませんがshould、仕様ごとにテストされる動作は1ビットのみである必要があります。

it 'should do foo and bar' do
  subject.do_foo.should be_true
  subject.do_bar.should be_true
end

悪いです-あなたは同時に2つの異なる振る舞いをスペックしています。

一方、2つのアサーションが1つのことのさまざまな側面を検証しているだけの場合は、たとえば、それで問題ありません。

it 'should return a prime integer' do
  result = subject.do_x
  result.should be_a(Integer)
  result.foo.should be_prime
end

私にとっては、整数を返すことをチェックする1つの仕様と、素数を返す別の仕様を用意することはあまり意味がありません。

もちろん、この場合、be_primeマッチャーはこれらのチェックの両方を簡単に実行できます-おそらく、カスタムマッチャーを使用して複数のアサーションを1に減らすことができれば、複数のアサーションは問題ありません(実際にこれを実行する価値があるかどうかはおそらく状況によって異なります)

あなたの特定のケースでは、2つの動作が行われていると主張することができます。1つはステータスを変更し、もう1つはideasコレクションを変更します。私はあなたのスペックを言い換えて、リリースメソッドが何をすべきかを言うでしょう-

it 'should change the status to available'
it 'should remove the idea from the claimants ideas'

現時点では、これらのことは常に同時に起こりますが、それらは別々の動作であると私は主張します-複数の人がアイデアを主張/リリースでき、最後の人がアイデアをリリースしたときにのみステータスが変わるシステムを簡単に想像できます。

于 2012-12-28T12:45:51.460 に答える
3

私も同じ問題を抱えています...それはテストに時間がかかり、あなたが言ったようにばかげているので、それはそれごとにすべきです(私の上司は言います)。テストには良識と柔軟性が必要です。そうしないと、奴隷になってしまう可能性があります。とにかく、私はあなたのテストに同意します。

于 2012-12-28T11:58:37.487 に答える
2

私の方針は、複数のアサーションを潜在的な問題の兆候であり、再考する価値があると常に見なすことですが、必ずしも間違っているとは限りません。おそらく、私が書いた仕様の3分の1は、何らかの理由で複数のアサーションを含んでいることになります。

複数のアサーションを持つことの問題の1つは、一方が失敗すると、もう一方が成功したかどうかを確認できないことです。これは、結果の配列を作成し、その配列の値をアサートすることで回避できる場合があります。

あなたが質問している場合、複数のアサーションが問題になるとは思いませんが、私には問題のように思われる何か他のものがあります。スペックが過度に結合されているようです。

あなたはどんなものの振る舞いも主張しようとしていると思いますが、テストはそれに対して何が行われたかを(最終的な価値として)伝えるためにjamesの振る舞いにも依存しています。代わりに私が通常行うことは、test-doubleを作成し、それが期待するメッセージを直接指定するために使用することです。si_title#statussi_title#should_receive

于 2012-12-28T13:21:24.593 に答える
2

Frederick Cheungは、特に理由について非常に良い答え(+1)を与えたと思いますがits、、、、および構文letsを使用するコードの比較ビットも示したいと思います。beforecontext

context "Given a si_title" do
  let(:james) { James.new } # or whatever
  before do
    james.claim(si_title)
    james.release(si_title)
  end
  context "That is valid" do
    let(:si_title) { Si_title.new } # or whatever

    describe "James' ideas" do
      subject { james.ideas }
      its(:size) { should == 0 }
      its(:whatever) { should == "Whatever" }
    end
    describe "Si title" do
      subject { si_title }
      its(:status) { should == "available" }
    end
  end
  context "That is invalid" do
    # stuff here
  end
end

さらに進んで期待値letsを作成し、次に例shared_examplesを作成して、さまざまな側面(null引数、無効な引数、不正なオブジェクトなど)をチェックするために使用できるようにしますが、これはあなたの意図を特定し、それでも繰り返しを減らすためのはるかに良い方法。フレデリックの答えから例をとると:

it 'should return a prime integer' do
  result = subject.do_x
  result.should be_a(Integer)
  result.foo.should be_prime
end

RSpecの構文を完全に使用すると、次のようになります。

  let(:result) { 1 }
  subject{ result }
  its(:do_x) { should be_a(Integer) }
  its(:foo) { should be_prime }

つまり、主題の複数の側面を確認できます。

于 2012-12-28T15:47:26.907 に答える