6

rspec を使い始めて以来、フィクスチャの概念に問題がありました。私の主な懸念事項は次のとおりです。

  1. テストを使用して、驚くべき動作を明らかにします。私は、テストしている例について考えられるすべてのエッジ ケースを列挙できるほど賢いわけではありません。ハードコードされたフィクスチャの使用は、私が想像した非常に特殊なケースでコードをテストするだけなので、制限があるようです。(確かに、どのケースをテストするかに関しては、私の想像力にも限界があります。)

  2. コードのドキュメントの形式としてテストを使用します。ハードコーディングされたフィクスチャ値がある場合、特定のテストが何を実証しようとしているのかを明らかにするのは困難です。例えば:

    describe Item do
      describe '#most_expensive' do
        it 'should return the most expensive item' do
          Item.most_expensive.price.should == 100
          # OR
          #Item.most_expensive.price.should == Item.find(:expensive).price
          # OR
          #Item.most_expensive.id.should == Item.find(:expensive).id
        end
      end
    end
    

    最初の方法を使用すると、最も高価なアイテムが何であるかが読者に示されず、その価格が 100 であることがわかります。3 つの方法はいずれも、そのフィクスチャ:expensiveが にリストされている最も高価なものであると信じるよう読者に求めますfixtures/items.ymlItem不注意なプログラマーは、 inを作成before(:all)したり、別のフィクスチャを に挿入したりして、テストを中断する可能性がありfixtures/items.ymlます。それが大きなファイルである場合、問題の原因を突き止めるのに長い時間がかかる可能性があります。

私が始めたことの 1 つは、#generate_randomすべてのモデルにメソッドを追加することです。このメソッドは、スペックを実行しているときにのみ使用できます。例えば:

class Item
  def self.generate_random(params={})
    Item.create(
      :name => params[:name] || String.generate_random,
      :price => params[:price] || rand(100)
    )
  end
end

(これを行う方法の具体的な詳細は、実際にはもう少しクリーンです。すべてのモデルの生成とクリーンアップを処理するクラスがありますが、このコードは私の例では十分に明確です。)したがって、上記の例では、次のようにテストできます。続きます。心のフェイントへの警告: 私のコードは次の使用に大きく依存していますbefore(:all):

describe Item do
  describe '#most_expensive' do
    before(:all) do
      @items = []
      3.times { @items << Item.generate_random }
      @items << Item.generate_random({:price => 50})
    end

    it 'should return the most expensive item' do
      sorted = @items.sort { |a, b| b.price <=> a.price }
      expensive = Item.most_expensive
      expensive.should be(sorted[0])
      expensive.price.should >= 50      
    end
  end
end

このようにして、私のテストは驚くべき動作をよりよく明らかにします。この方法でデータを生成すると、コードが予期したとおりに動作しないエッジ ケースに出くわすことがありますが、フィクスチャのみを使用していた場合はキャッチできなかったでしょう。たとえば、 の#most_expensive場合、複数のアイテムが最も高価な価格を共有するという特別なケースを処理するのを忘れると、最初の でテストが失敗することがありましたshould。AutoSpec で非決定論的な失敗を見ると、何かが間違っているという手がかりが得られます。フィクスチャだけを使用していた場合、そのようなバグを発見するのにさらに時間がかかる可能性があります。

また、私のテストでは、予想される動作がどのようなものかをコードで示すという、わずかに優れた仕事もしています。私のテストでは、sorted が価格の降順で並べ替えられたアイテムの配列であることが明らかになりました。その配列の最初の要素と等しいと予想#most_expensiveしているので、 の予想される動作がさらに明確になりmost_expensiveます。

それで、これは悪い習慣ですか?固定具に対する私の恐怖は不合理なものですか? モデルごとにメソッドを書くのはgenerate_random大変ですか? または、これは機能しますか?

4

12 に答える 12

14

このトピックや、 Jason Baker がリンクしているトピックでMonte Carlo Testingについて言及 した人が誰もいなかったことに驚いています。ランダム化されたテスト入力を広範囲に使用したのはこれだけです。ただし、テストケースごとに乱数ジェネレーターのシードを一定にすることで、テストを再現可能にすることが非常に重要でした。

于 2009-03-11T21:15:09.567 に答える
5

これについては、私の最近のプロジェクトでよく考えました。最終的に、次の 2 点に落ち着きました。

  • テストケースの再現性は最も重要です。ランダムなテストを書かなければならない場合は、それを広範囲に文書化する準備をしてください。
  • コード カバレッジの松葉杖としてランダム性を使用することは、適切なカバレッジがないか、代表的なテスト ケースを構成するものを知るのに十分なほどドメインを理解していないことを意味します。どちらが正しいかを判断し、それに応じて修正します。

要するに、ランダム性は多くの場合、価値があるよりも問題になる可能性があります。引き金を引く前に、正しく使用するかどうかを慎重に検討してください。私たちは最終的に、ランダムなテスト ケースは一般的には良くない考えであり、使用するにしても控えめに使用することを決定しました。

于 2009-03-11T21:11:31.213 に答える
2

すでに多くの優れた情報が投稿されていますが、ファズテストも参照してください。世間では、Microsoftは多くのプロジェクトでこのアプローチを使用していると言われています。

于 2009-03-11T21:16:29.703 に答える
1

Machinistをご覧になることをお勧めします。

http://github.com/notahat/machinist/tree/master

Machinistがデータを生成しますが、繰り返し可能であるため、各テスト実行には同じランダムデータが含まれます。

乱数ジェネレーターを一貫してシードすることで、同様のことができます。

于 2009-03-12T02:57:36.170 に答える
1

テストに関する私の経験は、ほとんどが C/Python/Java で書かれた単純なプログラムです。そのため、これが完全に当てはまるかどうかはわかりませんが、あらゆる種類のユーザー入力を受け入れることができるプログラムがある場合は常に、次のようなテストを含めます。ランダムな入力データ、または少なくともコンピューターによって予測不可能な方法で生成された入力データ。ユーザーが何を入力するかを推測することはできないためです。または、できますが、そうすると、その仮定をしないハッカーが、完全に見落としているバグを見つける可能性があります。マシン生成の入力は、テスト手順から人間の偏見を完全に排除するための、私が知っている最良の (唯一の?) 方法です。もちろん、失敗したテストを再現するには、テストを実行する前に、テスト入力をファイルに保存したり (テキストの場合) 印刷したりする必要があります。

于 2009-03-11T21:50:20.810 に答える
1

ランダム テストは、オラクルの問題に対する解決策がない限り、つまり、与えられた入力に対してソフトウェアの期待される結果がどれであるかを判断しない限り、悪い習慣です。

オラクルの問題を解けば、単純なランダム入力生成よりも一歩先へ進むことができます。ソフトウェアの特定の部分が単純なランダムよりも多く実行されるように、入力分布を選択できます。

次に、ランダム テストから統計テストに切り替えます。

if (a > 0)
    // Do Foo
else (if b < 0)
    // Do Bar
else
    // Do Foobar

範囲内でランダムに選択するaと、 50%、25%、25% の時間で運動します。またはよりも多くのバグが見つかる可能性があります。bintFooBarFoobarFooBarFoobar

a66.66% の確率でマイナスになるように選択した場合、最初の分配よりも多く行使されますBarFoobar実際、3 つのブランチはそれぞれ 33.33% の確率で実行されます。

もちろん、観察した結果が予想した結果と異なる場合は、バグを再現するのに役立つ可能性があるすべてをログに記録する必要があります。

于 2009-03-11T21:50:48.017 に答える
1

ランダムなテスト データの使用は優れた方法です。ハードコードされたテスト データは、明示的に考えたケースのみをテストしますが、ランダム データは、間違っている可能性のある暗黙の仮定を洗い流します。

これにはFactory Girlとffakerを使用することを強くお勧めします. (どのような状況でも、Rails フィクスチャを使用しないでください。)

于 2012-02-13T21:02:11.283 に答える
0

次のトピックも表示される場合があります: ランダムな入力を使用したテストのベスト プラクティス

于 2009-03-11T21:08:12.063 に答える