3

単体テストでランダムデータを生成することは一般的に悪い考えだと読みました(そしてその理由は理解しています)が、ランダムデータでテストしてから、バグを発見したランダムテストから固定ユニットテストケースを構築するのは良いことのようです。しかし、うまく整理する方法がわかりません。私の質問は、実際には特定のプログラミング言語や特定の単体テストフレームワークに関連していないため、Pythonといくつかの疑似単体テストフレームワークを使用します。これが私がそれをコーディングする方法です:

def random_test_cases():
   datasets = [
       dataset1,
       dataset2,
       ...
       datasetn
   ]
   for dataset in datasets:
       assertTrue(...)
       assertEquals(...)
       assertRaises(...)
       # and so on

問題は、このテストケースが失敗すると、どのデータセットが失敗の原因であるかを特定できないことです。私はそれを解決する2つの方法を見ます:

  1. データセットごとに1つのテストケースを作成します—問題はテストケースの負荷とコードの重複です。
  2. 通常、テストフレームワークでは、関数をアサートするためのメッセージを渡すことができます(私の例では、次のようなことができますassertTrue(..., message = str(dataset)))。問題は、そのようなメッセージを各アサートに渡す必要があることです。これもエレガントに見えません。

それを行うためのより簡単な方法はありますか?

4

5 に答える 5

6

私はまだそれは悪い考えだと思います。

ユニットテストは簡単である必要があります。同じコードと同じ単体テストが与えられれば、それを無限に実行でき、外部要因が関与しない限り、異なる応答が得られることはありません。これに反する目標は、自動化の保守コストを増加させ、目的を損ないます。

メンテナンスの面以外では、私には怠惰に思えます。機能を考慮し、ポジティブテストケースとネガティブテストケースを理解すれば、単体テストの開発は簡単です。

また、同じテストケース内で複数のテストケースを実行する方法を示すユーザーにも同意しません。テストが失敗した場合、どのテストが失敗したかをすぐに判断し、失敗した理由を知ることができるはずです。テストは、できる限り単純で、テスト対象のコードにできるだけ簡潔で関連性のあるものにする必要があります。

于 2012-07-19T05:21:49.680 に答える
2

列挙ではなく拡張によってテストを定義することも、単一のケースから複数のテストケースを呼び出すこともできます。

1つのテストケースから複数のテストケースを呼び出す:

MyTest()
{
    MyTest(1, "A")
    MyTest(1, "B")
    MyTest(2, "A")
    MyTest(2, "B")
    MyTest(3, "A")
    MyTest(3, "B")
}

また、一部のテストフレームワークを使用して、これを実現するための洗練された方法が存在する場合もあります。NUnitでそれを行う方法は次のとおりです

[Test, Combinatorial]
public void MyTest(
    [Values(1,2,3)] int x,
    [Values("A","B")] string s)
{
    ...
}
于 2012-07-19T03:28:42.523 に答える
2

また、それは悪い考えだと思います。

コードにランダムなデータをスローするのではなく、ユニットテストでそれを実行するようにしてください。そもそもなぜユニットテストを行うのかということです。答えは「コードの設計を推進すること」です。ランダムデータは、非常に堅固なパブリックインターフェイスに依存しているため、コードの設計を促進しません。気をつけてください、あなたはそれでバグを見つけることができます、しかしそれはユニットテストが何であるかではありません。そして、私が話しているのは単体テストであり、一般的なテストではないことに注意してください

そうは言っても、QuickCheckを確認することを強くお勧めします。Haskellなので、プレゼンテーションでは少し危険で、ドキュメントでは少し博士号を取得しますが、理解できるはずです。ただし、それがどのように機能するかを要約します。

テストするコード(たとえば関数)を選択した後、sort()保持する必要のある不変条件を確立します。この例では、次の場合に次の不変条件を使用できますresult = sort(input)

  • のすべての要素はresult、次の要素以下である必要があります。
  • のすべての要素は、同じ回数input存在する必要があります。result
  • resultそしてinput、同じ長さである必要があります(これは前の繰り返しですが、説明のために持っていきましょう)。

結果と出力を受け取り、それらの不変条件がコード化されているかどうかを確認する単純な関数で各バリアントをエンコードします。

次に、QuickCheckに生成方法を指示しますinput。これはHaskellであり、型システムはお尻を蹴るので、関数が整数のリストを取り、それらを生成する方法を知っていることがわかります。基本的に、ランダムな整数とランダムな長さのランダムなリストを生成します。もちろん、より複雑なデータ型(たとえば、正の整数のみ、正方形のみなど)がある場合は、よりきめ細かくすることができます。

最後に、これら2つがある場合は、QuickCheckを実行するだけです。それはすべてのものをランダムに生成し、不変条件をチェックします。失敗した場合は、どれが正確に表示されます。また、ランダムシードが表示されるため、必要に応じてこの正確な失敗を再実行できます。そして、追加のボーナスとして、失敗した不変条件を取得するたびに、入力を不変条件に失敗する可能な最小のサブセットに削減しようとします(ツリー構造について考えると、不変条件に失敗する最小のサブツリーに削減されます) )。

そして、あなたはそれを持っています。私の意見では、これがランダムデータを使ってテストを行う方法です。これは間違いなく単体テストではなく、別の方法で実行する必要があると思います(たとえば、変更のたびに実行するのではなく、CIに時々実行させる(すぐに遅くなるため))。繰り返しになりますが、これは単体テストとは異なる利点です。単体テストが設計を推進している間に、QuickCheckがバグを検出します。

于 2012-07-19T13:05:49.283 に答える
0

通常、ユニットテストフレームワークは、適切なアサーション方法を選択する限り、「有益な失敗」をサポートします。

ただし、他のすべてが機能しない場合は、データセットをコンソール/出力ファイルまで簡単にトレースできます。ローテクですが、動作するはずです。

[TestCaseSource("GetDatasets")]
public Test.. (Dataset d)
{
   Console.WriteLine(PrettyPrintDataset(d));
   // proceed with checks
   Console.WriteLine("Worked!");
}
于 2012-07-19T06:02:39.397 に答える
0

Rのクイックチェックでは、この問題を次のように解決しようとしました。

  • テストは実際には疑似ランダム(シードは固定)であるため、テスト結果をいつでも再現できます(もちろん外部要因を除く)
  • このtest関数は、失敗したアサーションや失敗したデータなど、エラーを再現するのに十分なデータを返します。reproの戻り値で呼び出される便利な関数testは、失敗したアサーションの開始時にデバッガーに到達し、引数は失敗の目撃者に設定されます。テストがバッチモードで実行される場合、同等の情報がファイルに保存され、それを取得するコマンドがstderrに出力されます。その後、以前と同じように呼び出すことができますrepro。あなたがRでプログラムするかどうかにかかわらず、これがあなたの要件に対応し始めるかどうか知りたいです。このソリューションのいくつかの側面は、動的性が低い言語やファーストクラスの関数を持たない言語では実装が難しい場合があります。
于 2015-02-12T20:52:54.413 に答える