単体テストのランダムデータ?


136

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

私はこれに対していくつかの異なる理由を彼に与えました、主なものは:

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

別の同僚が追加しました:

  • 例外をテストしている場合、ランダムな値では、テストが予期した状態になることは保証されません
  • ランダムデータは、ユニットテストではなく、システムのフラッシュアウトと負荷テストに使用されます

他の誰かが私に彼にこれをやめるように彼に与えることができる追加の理由を追加できますか?

(または、これは単体テストを書くための許容できる方法であり、私と私の同僚は間違っていますか?)


32
「ランダムな値は、テストが真に再現可能ではないことを意味します」テットは疑似乱数を使用するため、真ではありません。同じ初期シードを指定し、「ランダム」テストの同じシーケンスを取得します。
Raedwald、2011

11
逸話:CSVエクスポートクラスを書いたことがあり、ランダムなテストにより、制御文字がセルの最後に配置されたときにバグが明らかになりました。ランダムテストがなければ、これをテストケースとして追加することは考えられませんでした。常に失敗しましたか?いいえ、完璧なテストですか?いいえ。バグを見つけて修正するのに役立ちましたか?はい。
Tyzoid

1
テストはドキュメントとしても機能し、コードがいつ入力として期待され、何が出力として期待されるかを説明します。明確な任意のデータを使用してテストを行うことは、ランダムデータを生成するコードよりも簡単で、説明的です。
破片

ランダムに生成された値のためにユニットテストが失敗し、この値がアサートの一部ではない場合は、ユニットテストのデバッグに頑張ってください。
eriksmith200 2018年

回答:


72

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

プログラム(またはユニット)の仕様は、それを満たすプログラムが存在するという仮説です。プログラム自体はその仮説の証拠となります。ユニットテストは、プログラムが仕様どおりに機能することを否定する証拠を提供する試みです。

これで、単体テストを手動で作成できますが、これは実際には機械的な作業です。自動化できます。あなたがしなければならないのは仕様を書くことだけであり、そしてマシンはあなたのコードを壊そうとするたくさんのユニットテストを生成することができます。

使用している言語はわかりませんが、こちらをご覧ください。

Java http://functionaljava.org/

Scala(またはJava) http://github.com/rickynils/scalacheck

Haskell http://www.cs.chalmers.se/~rjmh/QuickCheck/

.NET:http//blogs.msdn.com/dsyme/archive/2008/08/09/fscheck-0-2.aspx

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

ハッピーテスト!


1
これに+1。ScalaCheckは、最小化されたランダムなテストデータを再現可能な方法で生成するという驚異的な仕事をします。
Daniel Spiewak 2008

17
ランダムではありません。それは任意です。大きな違い:)
Apocalisp 2008

reductiotest.orgはもう存在しているようで、Googleは私を他のどこにも向けませんでした。それが今どこにあるのか?
Raedwald、2011

現在は、Functional Javaライブラリの一部です。リンクを編集しました。しかし、私はScalacheckを使用してJavaコードをテストします。
Apocalisp、2011

ScalaCheckがGitHubに移転しました。回答のリンクを更新しました。Scalaだけでなく、Javaにも役立ちます。(以前のリンクはcode.google.com/p/scalacheckでした
RobertB 2013年

38

この種のテストは、モンキーテストと呼ばれます。正しく実行すると、非常に暗いコーナーからバグを吸い出すことができます。

再現性に関する懸念に対処するには、これに取り組む正しい方法は、失敗したテストエントリを記録し、特定のバグのファミリ全体を調査するユニットテストを生成することです。ユニットテストに、初期障害の原因となった(ランダムデータからの)1つの特定の入力を含めます。


26

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

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


4
たくさんのロックとスレッドがあるシステムでこれがうまく機能するのを見てきました。「ランダム」シードは実行ごとにファイルに書き込まれ、実行が失敗した場合は、コードがたどったパスを計算して、見逃したケースの手書きの単体テストを作成できます。
Ian Ringrose

PRNGは何の略ですか?
systemovich

疑似乱数ジェネレータ
ディーン

16

本「Beautiful Code」には、「Beautiful Tests」と呼ばれる章があり、バイナリ検索アルゴリズムのテスト戦略について説明しています。1つの段落は「ランダムなテストの行為」と呼ばれ、ランダムな配列を作成してアルゴリズムを徹底的にテストします。この本の一部は、Googleブックスの95ページでオンラインで読むことができますが、一見の価値がある素晴らしい本です。

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


16

私はランダムテストを支持し、それらを記述します。ただし、それらが特定のビルド環境で適切かどうか、およびそれらを含める必要があるテストスイートは、より微妙な問題です。

ローカルで(たとえば、開発ボックスで一晩)実行します。ランダム化されたテストにより、バグが明白で不明確であることが判明しました。あいまいなものは難解なので、ランダムなテストが実際にそれらをフラッシュする唯一の現実的なものだったと思います。テストとして、ランダム化されたテストで発見された見つけにくいバグを1つ選び、クラックの開発者にその機能(約数行のコード)をレビューしてもらいました。誰もそれを検出することができませんでした。

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

私が議論することはできませんが、あなたがする1つのポイントは、それがビルドシステムを壊すということです。ほとんどのビルドおよび継続的インテグレーションテストは、テストが毎回同じことを実行することを期待しています。したがって、ランダムに失敗するテストは混乱を引き起こし、ランダムに失敗し、無害な変更に指を向けます。

その場合の解決策は、ビルドおよびCIテストの一部としてランダム化されたテストを実行することですが、固定されたシードを使用して、固定された反復回数で実行します。したがって、テストは常に同じことを行いますが、それでも入力スペースの束を調査します(複数の反復で実行した場合)。

ローカルで、たとえば、関係するクラスを変更する場合、より多くの反復または他のシードで自由に実行できます。ランダム化されたテストがますます普及するようになると、ランダムであることがわかっているテストの特定のスイートを想像することもできます。テストはさまざまなシードで実行できるため(時間の経過とともにカバレッジが増加します)、失敗が同じことを意味しない場合もあります。確定的CIシステムとして(つまり、実行はコードの変更と1対1で関連付けられていないため、問題が発生したときに特定の変更を指差す必要はありません)。

ランダム化されたテスト、特に適切に記述されたテストについては、言うべきことがたくさんあります。


14

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

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

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

ランダム変数を使用する場合、その変数に基づいてテストをフォークすると、においがすることになります。


10

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


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

テストケースがテスト対象を正確に記録しない場合は、おそらくテストケースを書き直す必要があります。静的データとランダムデータのどちらを使用した場合でも、何が原因で失敗したのかを正確に把握できるように、テストケースで参照できるログが常に必要です。


9

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


2
しかし、これは単体テストとは根本的に異なるものではありませんか?別の時間に行われましたか?
内部石2014年

1
@endolith物理法則がないため、特定の時間に特定のテストを実行する必要があります
user253751

1
@immibisしかし、特定の時間に特定のテストを行うことには十分な理由があります。ユーザーが「OK」ボタンをクリックするたびに一連の単体テストを実行するわけではありません。
エンドリス、2015年

5

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

思いもよらなかったケースをテストするためにランダムデータを作成することの価値は間違いなくわかりますが、そうです、ランダムに合格または失敗する可能性のある単体テストは悪いことです。


5

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


ファジングにランダム入力を使用することは、可能な場合、カバレッジガイド付きファジングの代替としては不十分です。
gobenji

1

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

「任意」のデータの概念は重要ではないものを示す方法としても非常に役立ちます。私たちは、当面のテストに関係のないノイズデータがたくさんある場合に思い浮かぶいくつかの受け入れテストがあります。


0

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


0

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

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

それでも資格はランダムですか?ビールについて話し合いましょう。:-)


0

テストデータの問題に対する3つの解決策を想定できます。

  • 固定データでテストする
  • ランダムデータでテストする
  • ランダムデータを1回生成し、固定データとして使用する

上記のすべてを行うことをお勧めします。つまり、脳を使用して解決されたいくつかのエッジケースと、一度だけ生成するランダム化されたデータの両方を使用して、繰り返し可能な単体テストを記述します。次に、同様に実行するランダム化されたテストのセットを記述します。

ランダム化されたテストは、繰り返し可能なテストで見逃したものをキャッチすることを期待してはなりません。反復可能なテストですべてをカバーすることを目指し、ランダム化されたテストをボーナスと見なす必要があります。彼らが何かを見つけた場合、それはあなたが合理的に予測できなかったものでなければなりません。本物のオッドボール。


-2

修正したかどうかを確認できなかった場合、どうすればもう一度テストを実行できますか?すなわち、彼はテストの再現性を失います。

他の回答で述べたように、テストでランダムなデータの負荷を逃がすことにはおそらく何らかの価値があると思いますが、それは他のものよりも負荷テストの見出しの下にあります。これは、ほとんど「希望ごとのテスト」のプラクティスです。私は実際には、あなたの男は彼がテストしようとしていることについて単に考えていないと思います、そしてランダム性が最終的にいくつかの神秘的なエラーをトラップすることを望んで、その考えの欠如を補います。

だから私が彼と一緒に使うであろう議論は彼が怠惰であるということです。あるいは、言い換えれば、彼がテストしようとしていることを理解するのに時間がかからない場合、おそらく、彼が書いているコードを本当に理解していないことを示しています。


3
テストを再現できるように、ランダムデータまたはランダムシードをログに記録することができます。
cbp 2009年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.