質問

私には、フィールドにランダムなデータを入力するオブジェクトの単体テストを作成する同僚がいます。彼の理由は、通常のテストでは 1 つの静的な値しか使用しないのに対し、このテストではさまざまな値をテストするため、テストの範囲が広くなるからです。

私は彼にこれに反対するさまざまな理由をいくつか挙げましたが、主なものは次のとおりです。

  • ランダムな値は、テストが真に再現可能ではないことを意味します (これは、テストがランダムに失敗する可能性がある場合、ビルド サーバー上で失敗してビルドが中断される可能性があることも意味します)。
  • それがランダムな値でテストが失敗した場合は、a) オブジェクトを修正し、b) 毎回その値を強制的にテストする必要があります。そうすれば、それが機能することはわかりますが、ランダムであるため、値が何であったかはわかりません。

別の同僚はこう付け加えた。

  • 例外をテストしている場合、ランダムな値ではテストが期待どおりの状態になるかどうかは保証されません。
  • ランダム データは、単体テストではなく、システムのフラッシュアウトと負荷テストに使用されます。

他の誰かが彼にこれをやめさせるために私が彼にできる追加の理由を追加できますか?

(あるいは、これは単体テストを作成する許容可能な方法であり、私と他の同僚は間違っているのでしょうか?)

役に立ちましたか?

解決

妥協点はあります。あなたの同僚は実際には何かに取り組んでいますが、私は彼のやり方が間違っていると思います。完全にランダムなテストが非常に役立つかどうかはわかりませんが、無効ではないことは確かです。

プログラム(またはユニット)の仕様は、それを満たす何らかのプログラムが存在するという仮説です。つまり、プログラム自体がその仮説の証拠となります。単体テストは、プログラムが仕様に従って動作することを否定する反証を提供する試みであるべきです。

単体テストを手動で作成できるようになりましたが、実際には機械的な作業になります。自動化することができます。仕様を記述するだけで、マシンはコードを破壊しようとする大量の単体テストを生成できます。

どの言語を使用しているかわかりませんが、ここを参照してください。

ジャワhttp://functionjava.org/

スカラ (または Java)http://github.com/rickynils/scalacheck

ハスケルhttp://www.cs.chalmers.se/~rjmh/QuickCheck/

。ネット:http://blogs.msdn.com/dsyme/archive/2008/08/09/fscheck-0-2.aspx

これらのツールは、整形式の仕様を入力として受け取り、自動生成されたデータを使用して、必要な数だけ単体テストを自動的に生成します。彼らは「縮小」戦略 (調整可能) を使用して、コードを破壊する最も単純なテスト ケースを見つけ、それがエッジ ケースを適切にカバーしていることを確認します。

テストを楽しんでください。

他のヒント

この種のテストは サルテスト. 。正しく行えば、本当に暗い隅から虫を煙で追い出すことができます。

再現性に関する懸念に対処するには:これに取り組む正しい方法は、失敗したテスト エントリを記録し、単体テストを生成して、 家族全員 特定のバグについて。そして、初期エラーの原因となった 1 つの特定の入力 (ランダム データから) を単体テストに含めます。

ここには、PRNG に定数をシードするために使用できる中間の家があります。これにより、反復可能な「ランダム」データを生成できます。

個人的には、(一定の) ランダム データがテストに役立つ場合があると思います。慎重に考え抜いたコーナーをすべて完了したと思った後、PRNG からの刺激を使用すると、他のことが見つかることがあります。

本の中で 美しいコード, 、「美しいテスト」と呼ばれる章があり、そこで彼は、 二分探索 アルゴリズム。ある段落は「テストのランダム行為」と呼ばれるもので、ランダムな配列を作成してアルゴリズムを徹底的にテストします。この内容の一部は、Google Books でオンラインで読むことができます。 95ページ, 、しかし、それは持っている価値のある素晴らしい本です。

したがって、これは基本的に、テスト用にランダム データを生成することが実行可能なオプションであることを示しているだけです。

テストを見る人にとっての利点の 1 つは、任意のデータが明らかに重要ではないことです。数十のデータを含むテストをあまりにも多く見てきましたが、何がそのようにする必要があるのか​​、何がたまたまそのようになっているのかを判断するのは難しい場合があります。例えば。住所検証方法が特定の郵便番号でテストされ、他のすべてのデータがランダムであれば、郵便番号が唯一の重要な部分であるとほぼ確信できます。

TDD を実行している場合、ランダム データは優れたアプローチであると私は主張します。テストが定数を使用して記述されている場合、コードが特定の値に対して動作することのみを保証できます。テストがランダムにビルド サーバーに失敗する場合は、テストの作成方法に問題がある可能性があります。

ランダム データは、将来のリファクタリングが魔法の定数に依存しないようにするのに役立ちます。結局のところ、テストがドキュメントである場合、定数の存在は、その定数に対してのみ機能する必要があることを意味していませんか?

大げさですが、私は「この変数の値がこのテストの結果に影響を与えてはならない」という兆候としてテストにランダムなデータを挿入することを好みます。

ただし、確率変数を使用し、その変数に基づいてテストをフォークする場合、それは臭いです。

  • それがランダムな値でテストが失敗した場合は、a) オブジェクトを修正し、b) 毎回その値を強制的にテストする必要があります。そうすれば、それが機能することはわかりますが、ランダムであるため、値が何であったかはわかりません。

テスト ケースがテスト内容を正確に記録していない場合は、テスト ケースを再コーディングする必要がある可能性があります。静的データを使用した場合でもランダム データを使用した場合でも、失敗の原因を正確に把握できるように、テスト ケースで参照できるログを常に保持したいと考えています。

あなたの同僚はやっています ファズテスト, 、彼はそれについて知りませんが。これらはサーバー システムで特に価値があります。

私はランダムテストに賛成であり、テストを書いています。ただし、それらが特定のビルド環境に適切であるかどうか、またどのテスト スイートに含めるべきかについては、より微妙な問題になります。

ローカルで (たとえば、開発ボックスで一晩中) ランダム化テストを実行すると、明らかなバグと不明瞭なバグの両方が見つかりました。あいまいなものは非常に難解であるため、それらを排除するための現実的な方法はランダムなテストだけだったと思います。テストとして、ランダム化テストで発見された発見が難しいバグを 1 つ取り上げ、6 人のクラック開発者にそのバグが発生した関数 (約 12 行のコード) をレビューしてもらいました。誰もそれを検出できませんでした。

ランダム化されたデータに対するあなたの議論の多くは、「テストには再現性がない」という性質のものです。ただし、適切に作成されたランダム化テストでは、ランダム化シードの開始に使用されたシードがキャプチャされ、失敗時に出力されます。これにより、手動でテストを繰り返すことができるだけでなく、テストのシードをハードコーディングすることで特定の問題をテストする新しいテストを簡単に作成することができます。もちろん、そのケースをカバーする明示的なテストを手動でコーディングする方がよいでしょうが、怠惰には利点があり、これにより、失敗したシードから新しいテスト ケースを基本的に自動生成することもできます。

ただし、私が議論できない点の 1 つは、それがビルド システムを破壊するということです。ほとんどのビルド テストと継続的統合テストでは、テストが毎回同じことを実行することが期待されます。したがって、ランダムに失敗するテストは混乱を引き起こし、ランダムに失敗し、無害な変更を指摘することになります。

したがって、解決策は、ビルドおよび CI テストの一部としてランダム化テストを実行することですが、 固定シードを使用して、固定回数の反復で実行します。. 。したがって、テストは常に同じことを行いますが、依然として大量の入力空間を探索します (複数の反復で実行する場合)。

ローカルでは、たとえば関連するクラスを変更する場合、それをさらに繰り返したり、他のシードを使用して自由に実行できます。ランダム化されたテストがより一般的になった場合、ランダムであることが知られている特定のテスト スイートを、異なるシードで実行でき (したがって、時間の経過とともにカバレッジが増加し)、失敗しても同じ意味がなくなることを想像することもできるでしょう。決定論的な CI システムとして (つまり、実行はコード変更と 1 対 1 で関連付けられていないため、失敗したときに特定の変更を指摘する必要はありません)。

ランダム化されたテスト、特によく書かれたテストには言うべきことがたくさんあるので、すぐに無視しないでください。

ランダムなデータを一度 (テスト実行ごとに 1 回ではなく、正確に 1 回という意味です) 生成して、それ以降のすべてのテストで使用できますか?

思いもよらなかったケースをテストするためにランダムなデータを作成することの価値は確かにわかりますが、ランダムに合格または不合格になる単体テストがあるのは悪いことです。

テストの目標は何なのかを自問する必要があります。
単体テスト ロジック、コード フロー、オブジェクトの相互作用を検証することです。ランダムな値を使用すると、別の目標を達成しようとするため、テストの焦点と単純さが低下します。読みやすさの理由 (UUID、ID、キーなどの生成) からは許容されます。
特に単体テストの場合、この方法で問題の発見に成功したことは一度たりともありません。しかし、私は、ランダムな値、主にランダムな日付を巧みに使おうとする多くの決定論問題 (テストで) を見てきました。
ファズテストは次の場合に有効なアプローチです。 統合テスト そして エンドツーエンドのテスト.

テストにランダム入力を使用している場合は、値が何であるかを確認できるように入力をログに記録する必要があります。こうすることで、特殊なケースに遭遇した場合でも、 できる それを再現するためのテストを作成します。ランダム入力を使用しない同じ理由を人々から聞いたことがありますが、特定のテスト実行に使用される実際の値を理解していれば、それほど問題ではありません。

「任意の」データの概念は、何かを示す方法としても非常に役立ちます。 ない 重要。現在のテストとは無関係なノイズ データが大量に含まれる受け入れテストがいくつかあります。

オブジェクト/アプリによっては、ランダム データが負荷テストに使用される場合があります。データの境界条件を明示的にテストするデータを使用することがより重要だと思います。

今日まさにこれに遭遇しました。私は欲しかった 擬似ランダム (つまり、サイズの点では圧縮されたオーディオ データのように見えます)。私も欲しかったTODOしました 決定的な. 。rand() は OSX と Linux では異なりました。そして、再シードしない限り、いつでも変更される可能性があります。そこで、決定論的でありながら擬似ランダムになるように変更しました。テストは、定型データを使用するのと同じくらい再現可能です (ただし、より便利に記述できます)。

これは。。。でした ない コードパスを介したランダムなブルートフォースによるテスト。それが違いです:まだ決定論的であり、再現可能であり、実際の入力のように見えるデータを使用して、複雑なロジックのエッジ ケースに対して一連の興味深いチェックを実行します。まだ単体テスト中。

それはまだランダムですか?ビールを飲みながら話しましょう。:-)

テスト データの問題に対する解決策は 3 つ考えられます。

  • 固定データでテストする
  • ランダムなデータでテストする
  • ランダムデータを生成する 一度, 固定データとして使用します

することをお勧めします 上記のすべて. 。つまり、頭を使って考え出したいくつかのエッジケースと、一度だけ生成するランダム化されたデータの両方を使用して、反復可能な単体テストを作成します。次に、実行するランダム化されたテストのセットを作成します。 同じように.

ランダム化されたテストは、反復可能なテストが見逃しているものを見つけることを決して期待すべきではありません。反復可能なテストですべてをカバーすることを目指し、ランダム化されたテストはおまけであると考える必要があります。もし彼らが何かを見つけたとしたら、それはあなたが合理的に予測できなかったものであるはずです。まさに変わり者。

テストが失敗したかどうかを確認できなかった場合、どうすればテストを再度実行できるでしょうか?つまり、テストの再現性が失われます。

おそらくテスト時にランダムなデータの負荷を投げることにはある程度の価値があると思いますが、他の返信で述べられているように、それは他の何よりも負荷テストの範疇に当てはまります。これはまさに「希望によるテスト」の実践です。実際には、あなたの男性は何をテストしようとしているのかについて考えていないだけで、ランダム性が最終的に何らかの不可解なエラーをトラップすることを期待することでその考えの欠如を補っていると思います。

したがって、私が彼に対して使う議論は、彼は怠け者だということです。あるいは、別の言い方をすると、テストしようとしているものを理解するのに時間がかからない場合、それはおそらく彼が書いているコードを本当に理解していないことを示しています。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top