本質的にランダム/非決定的アルゴリズムの単体テスト


41

私の現在のプロジェクトは、簡潔に「制約のあるランダムなイベント」の作成に関係しています。私は基本的に検査のスケジュールを生成しています。それらのいくつかは、厳密なスケジュールの制約に基づいています。金曜日の午前10:00に週に1回検査を行います。他の検査は「ランダム」です。「検査は週に3回行う必要がある」、「検査は午前9時から午後9時までの間に行う必要がある」、「同じ8時間内に2つの検査を行うべきではない」などの基本的な構成可能な要件がありますが、特定の検査セットに対して設定された制約の範囲内で、結果の日付と時刻は予測できません。

ユニットテストとTDD、IMOは、このシステムで大きな価値があります。これは、要件の完全なセットがまだ不完全でありながら、段階的にビルドするために使用できるためです。現在、私が必要だとは知らない。厳密なスケジュールは、TDDにとって欠かせないものでした。ただし、システムのランダムな部分のテストを作成するときに、テスト対象を実際に定義するのは難しいと感じています。スケジューラによって生成されるすべての時間は制約内に収まる必要があると断言できますが、実際の時間を非常に「ランダム」にせずにこのようなすべてのテストに合格するアルゴリズムを実装できます。実際、それはまさに起こったことです。時刻は、正確には予測できませんが、許容される日付/時刻範囲の小さなサブセットに分類される問題を発見しました。アルゴリズムは、私が合理的に行うことができると思うすべてのアサーションに合格し、そのような状況で失敗する自動テストを設計することはできませんでしたが、「よりランダムな」結果が与えられると合格します。既存のテストを再構築して何度も繰り返すことで問題が解決したことを実証し、生成された時間が完全に許容範囲内に収まったことを視覚的に確認する必要がありました。

非決定論的な動作を期待するテストを設計するためのヒントはありますか?


すべての提案に感謝します。主な意見は、決定論的で、再現性があり、主張可能な結果を​​得るために、決定論的テストが必要だということです。理にかなっています。

制約プロセス(任意の長いバイト配列が最小と最大の間で長くなるプロセス)の候補アルゴリズムを含む一連の「サンドボックス」テストを作成しました。次に、そのコードをFORループで実行し、アルゴリズムにいくつかの既知のバイト配列(開始時に1から10,000,000の値)を与え、アルゴリズムにそれぞれ1009から7919の間の値に制約させます(素数を使用して、アルゴリズムは、入力範囲と出力範囲の間で偶然のGCFを通過しません)。結果の制約値がカウントされ、ヒストグラムが作成されます。「パス」するには、すべての入力をヒストグラム内に反映する必要があり(「失わない」ようにするための健全性)、ヒストグラム内の2つのバケットの差は2より大きくすることはできません(実際には<= 1でなければなりません) 、しばらくお待ちください)。勝ったアルゴリズムがあれば、それをカットして実動コードに直接貼り付け、永続的なテストを実施して回帰することができます。

コードは次のとおりです。

    private void TestConstraintAlgorithm(int min, int max, Func<byte[], long, long, long> constraintAlgorithm)
    {
        var histogram = new int[max-min+1];
        for (int i = 1; i <= 10000000; i++)
        {
            //This is the stand-in for the PRNG; produces a known byte array
            var buffer = BitConverter.GetBytes((long)i);

            long result = constraintAlgorithm(buffer, min, max);

            histogram[result - min]++;
        }

        var minCount = -1;
        var maxCount = -1;
        var total = 0;
        for (int i = 0; i < histogram.Length; i++)
        {
            Console.WriteLine("{0}: {1}".FormatWith(i + min, histogram[i]));
            if (minCount == -1 || minCount > histogram[i])
                minCount = histogram[i];
            if (maxCount == -1 || maxCount < histogram[i])
                maxCount = histogram[i];
            total += histogram[i];
        }

        Assert.AreEqual(10000000, total);
        Assert.LessOrEqual(maxCount - minCount, 2);
    }

    [Test, Explicit("sandbox, does not test production code")]
    public void TestRandomizerDistributionMSBRejection()
    {
        TestConstraintAlgorithm(1009, 7919, ConstrainByMSBRejection);
    }

    private long ConstrainByMSBRejection(byte[] buffer, long min, long max)
    {
        //Strip the sign bit (if any) off the most significant byte, before converting to long
        buffer[buffer.Length-1] &= 0x7f;
        var orig = BitConverter.ToInt64(buffer, 0);
        var result = orig;
        //Apply a bitmask to the value, removing the MSB on each loop until it falls in the range.
        var mask = long.MaxValue;
        while (result > max - min)
        {
            mask >>= 1;
            result &= mask;
        }
        result += min;

        return result;
    }

    [Test, Explicit("sandbox, does not test production code")]
    public void TestRandomizerDistributionLSBRejection()
    {
        TestConstraintAlgorithm(1009, 7919, ConstrainByLSBRejection);
    }

    private long ConstrainByLSBRejection(byte[] buffer, long min, long max)
    {
        //Strip the sign bit (if any) off the most significant byte, before converting to long
        buffer[buffer.Length - 1] &= 0x7f;
        var orig = BitConverter.ToInt64(buffer, 0);
        var result = orig;

        //Bit-shift the number 1 place to the right until it falls within the range
        while (result > max - min)
            result >>= 1;

        result += min;
        return result;
    }

    [Test, Explicit("sandbox, does not test production code")]
    public void TestRandomizerDistributionModulus()
    {
        TestConstraintAlgorithm(1009, 7919, ConstrainByModulo);
    }

    private long ConstrainByModulo(byte[] buffer, long min, long max)
    {
        buffer[buffer.Length - 1] &= 0x7f;
        var result = BitConverter.ToInt64(buffer, 0);

        //Modulo divide the value by the range to produce a value that falls within it.
        result %= max - min + 1;

        result += min;
        return result;
    }

...そして結果は次のとおりです。

ここに画像の説明を入力してください

LSB拒否(番号が範囲内に入るまでビットシフトする)は、非常にわかりやすい理由で、ひどいものでした。最大値未満になるまで任意の数を2で除算すると、すぐに終了します。重要な範囲ではない場合は、結果が上位3分の1に偏ります(ヒストグラムの詳細な結果に見られるように) )。これはまさに完成した日付から見た振る舞いでした。すべての時間は非常に特定の日の午後でした。

MSB拒否(範囲内になるまで一度に1つずつ最上位ビットを削除する)の方が優れていますが、繰り返しますが、各ビットで非常に大きな数を切り落とすため、均等に分散されません。上端と下端に数字が表示される可能性は低いため、中央の3分の1にバイアスがかかります。これは、ランダムデータを釣鐘曲線に「正規化」したい人にとっては有益かもしれませんが、2つ以上の小さな乱数の合計(サイコロを投げるのに似ています)は、より自然な曲線を与えます。私の目的では、失敗します。

このテストに合格した唯一の方法は、モジュロ除算によって制約することでした。これは、3つのうちで最速であることが判明しました。Moduloは、その定義により、利用可能な入力が与えられると、できるだけ均等な分布を生成します。


2
したがって、最終的には、乱数ジェネレータの出力を調べて、それがランダムかどうかを判断するプログラムが必要ですか?「5,4,10,31,120,390,2,3,4」はランダムでしたが、「49,39,1,10,103,12,4,189」はランダムではありませんでしたか?
psr

いいえ。ただし、実際のP​​RNGと最終結果の間にバイアスを導入しないようにすることをお勧めします。
キース

それから、PRNGをあざけることはOKのように聞こえます。値をマングルしないことをテストするために実際のランダムな値は必要ありません。ランダムな値を許容範囲の小さすぎるサブセットに絞り込んでいるバグがある場合は、特定の値が間違っている可能性があります。
psr

組み合わせもテストする必要があります。1時間あたりの予想検査がほぼ同じであっても、たとえば火曜日の午前11時の検査に続いて木曜日の午後2時と金曜日の午前10時が常に続く場合は保護されません。
デビッドソーンリー

これは、PRNG自体のテストです。上記の構造の制約メカニズムのテストは、完全に非ランダムなデータのセットが与えられているため、常にこのようなテストに失敗します。制約メカニズムがランダムデータの「順序付け」を試みていないと仮定すると、ユニットテストでは実行できない「テスト外部」を呼び出すことになります。
キース

回答:


17

ここで実際にテストしたいのは、ランダマイザーからの特定の結果セットが与えられると、メソッドの残りの部分が正しく実行されることです。

それがあなたが探しているものなら、テストの領域内で決定論的にするために、ランダマイザーをモックアウトします。

私は通常、GUIDジェネレーターやDateTime.Nowを含む、あらゆる種類の非決定的または予測不可能な(テスト作成時)データのモックオブジェクトを持っています。

編集、コメントから:可能な限り低いレベルでPRNG(その用語は昨夜私をエスケープしました)をモックする必要があります。バイト配列をInt64に変換した後ではなく、バイト配列を生成するとき。または、両方のレベルでも、Int64配列への変換が意図したとおりに動作することをテストし、DateTimes配列への変換が意図したとおりに動作することを個別にテストできます。Jonathonが言ったように、セットシードを与えるだけでそれを行うことができます。または、返すバイトの配列を与えることもできます。

PRNGのフレームワークの実装が変更されても壊れないので、後者を好む。ただし、シードを提供することの利点の1つは、実稼働で意図したとおりに機能しないケースを見つけた場合、アレイ全体ではなく、1つの番号を記録するだけで複製できることです。

これはすべて、理由から疑似乱数ジェネレーターと呼ばれることを覚えておく必要があります。そのレベルでさえ、いくらかのバイアスがあるかもしれません。


1
いいえ。この場合にテストするのはランダマイザー自体であり、ランダマイザーによって生成された「ランダム」値は指定された制約内に収まる一方で、許容範囲全体の不均一な分布に偏らないように、「ランダム」のままであることを表明します時間範囲。特定の日付/時刻が特定の制約を正しく通過または失敗することを決定論的にテストできますが、実際の問題は、ランダマイザーによって生成された日付が偏っており、したがって予測可能であるということでした。
キース

私が考えることができる唯一のことは、ランダマイザーに多数の日付を吐き出させてヒストグラムを作成し、値が比較的均等に分布していると断言することです。本当にランダムなデータのセットは、大きなセットが反論するという明らかなバイアスを示す可能性があるため、非常に手間がかかり、決定論的ではありません。
キース

1
これは、時折、予想外に壊れるテストです。あなたはそれを望んでいません。正直に言うと、あなたは私の主張を誤解していると思います。ランダマイザーと呼んでいるもののどこかに、乱数を生成するコード行が必要です その行は、私がランダマイザーと呼んでいるものであり、あなたがランダマイザーと呼んでいるもの(「ランダム」データに基づく日付の分布)は、テストしたいものです。それとも何か不足していますか?
pdr

ランダムシーケンスの適切な統計メトリック(相関、ブロック相関、平均、標準偏差など)は、本当に小さなサンプルを実行する場合にのみ期待の範囲を満たせません。サンプリングセットを増やすか、許容されるエラーバーを増やす
ラーシャー

1
「そのレベルでもある程度のバイアス」適切なPRNGを使用すると、実際のランダム性とは区別できるテスト(現実的な計算範囲)を見つけることができなくなります。したがって、実際には、優れたPRNGにバイアスはまったくないと想定できます。
CodesInChaos

23

これは愚かな答えのように聞こえますが、私はそれを前に見たことがあるので、私はそこにそれを投げるつもりです:

コードをPRNGから切り離します-ランダム化シードをランダム化を使用するすべてのコードに渡します。次に、単一のシードから「作業」値を決定できます(または、その複数のシードにより気分が良くなります)。これにより、多数の法則に依存することなく、コードを適切にテストできます。

それは奇妙に聞こえますが、これは軍隊がそれを行う方法です(それまたは彼らは実際にはまったくランダムではない「ランダムテーブル」を使用します)


正確に:アルゴリズムの要素をテストできない場合は、それを抽象化し、モックします
スティーブグレーレックス

確定的なシード値を指定しませんでした。代わりに、「ランダム」要素を完全に削除したため、特定のPRNGアルゴリズムに依存する必要すらありませんでした。広範囲に均等に分布した数値を与えられた場合、私が行ったアルゴリズムはバイアスを導入することなくそれらをより小さい範囲に制限できることをテストできました。PRNG自体は、開発者が適切にテストする必要があります(私はRNGCryptoServiceProviderを使用しています)。
キース

「ランダムテーブル」アプローチに関しては、「可逆」数生成アルゴリズムを含むテスト実装を使用することもできます。これにより、PRNGを「巻き戻し」たり、クエリして最後のN個の出力を確認したりできます。特定のシナリオでは、より詳細なデバッグが可能になります。
ダリエン

これはそれほど愚かではありません-それは、Googleが彼らの論文によると、スパナーテストで障害挿入を再現するために使用するのと同じ方法です:)
Akshat Mahajan

6

「それはランダムですか(十分)」は、非常に微妙な質問であることがわかりました。簡単な答えは、従来の単体テストではうまくいかないということです-一連のランダムな値を生成し、それらをさまざまな統計的テストに提出する必要があります。

パターンがあります-結局、擬似乱数ジェネレーターを使用しています。しかし、ある時点で物事はあなたのアプリケーションにとって「十分な」ものになります(十分に良いところでは、一方のゲームとゲームの間でLOTが異なります。比較的単純なジェネレーターで十分です。決意の整った攻撃者による)。

ウィキペディアの記事http://en.wikipedia.org/wiki/Randomness_testsおよびそのフォローアップリンクには、詳細情報があります。


平凡なPRNGでさえ、統計テストではパターンを表示しません。優れたPRNGの場合、実際の乱数と区別することは事実上不可能です。
CodesInChaos

4

二つの答えがあります。

===最初の回答===

あなたの質問のタイトルを見るとすぐに、私は飛び込んで解決策を提案するようになりました。私の解決策は、他のいくつかの人が提案したものと同じでした:あなたの乱数ジェネレータをモックアウトすること。結局のところ、適切な単体テストを作成するためにこのトリックを必要とするいくつかの異なるプログラムを構築し、すべてのコーディングで乱数へのモック可能なアクセスを標準的な慣行にし始めました。

しかし、それから私はあなたの質問を読みました。そして、あなたが説明する特定の問題については、それは答えではありません。問題は、乱数を使用するプロセスを予測可能にする必要があるということではありませんでした(したがって、テスト可能になります)。むしろ、問題は、アルゴリズムがRNGからの一様にランダムな出力をアルゴリズムからの制約内の一様な出力にマッピングしたことを確認することでした-基礎となるRNGが均一だった場合、検査時間が均等に分散されることを確認します(問題の制約)。

それは本当に難しい(しかしかなり明確に定義された)問題です。これは興味深い問題だということです。すぐにこれを解決するためのいくつかの本当に素晴らしいアイデアを考え始めました。私がホットショットプログラマーだった頃、私はこれらのアイデアで何かを始めたかもしれません。しかし、私はもうホットショットプログラマーではありません。

それで、難しい問題に飛び込む代わりに、私は自分自身に考えました:これの価値は何ですか?そしてその答えは期待外れでした。あなたのバグはすでに解決されており、今後この問題について熱心に取り組むでしょう。外部環境では問題をトリガーできず、アルゴリズムの変更のみが可能です。この興味深い問題に取り組む唯一の理由は、TDD(Test Driven Design)の実践を満たすためです。私が学んだことを一つのことがあれば、それは盲目的に付着していることである任意の、それは貴重な原因の問題ではないときの練習。私の提案はこれです:このためのテストを書かないで、先に進んでください。


=== 2番目の回答===

わあ...なんてクールな問題!

ここで行う必要があるのは、検査の日時を選択するアルゴリズムが、使用するRNGが均一に分布した数値を生成する場合、(問題の制約内で)均一に分布する出力を生成することを検証するテストを記述することです。難易度別に分類されたいくつかのアプローチがあります。

  1. ブルートフォースを適用できます。入力として実際のRNGを使用して、アルゴリズムを何回も実行するだけです。出力結果を調べて、それらが均一に分布しているかどうかを確認します。分布が一定のしきい値を超えて完全に均一である場合、テストを失敗させる必要があり、問題を確実にキャッチするために、しきい値を低く設定することはできません。つまり、誤検出(ランダムチャンスによるテストの失敗)の確率が非常に小さい(中規模のコードベースでは1%未満、さらには大きなコードベース)。

  2. アルゴリズムを、すべてのRNG出力の連結を入力として受け取り、検査時間を出力として生成する関数として考えてください。この関数が区分的に連続であることを知っている場合、プロパティをテストする方法があります。RNGをモック可能なRNGに置き換え、アルゴリズムを何度も実行して、均一に分散されたRNG出力を生成します。したがって、コードがそれぞれ[0..1]の範囲の2つのRNG呼び出しを必要とする場合、アルゴリズムを100回実行し、値[(0.0,0.0)、(0.0,0.1)、(0.0、 0.2)、...(0.0,0.9)、(0.1,0.0)、(0.1,0.1)、...(0.9,0.9)]。次に、100回の実行の出力が許容範囲内で(ほぼ)均一に分布しているかどうかを確認できます。

  3. 本当に信頼性の高い方法でアルゴリズムを検証する必要があり、アルゴリズムについての仮定を立てることができない場合、または何度も実行する場合、問題を攻撃することはできますが、アルゴリズムのプログラミング方法にいくつかの制約が必要になる場合があります。例として、PyPyとそのオブジェクト空間アプローチをご覧ください。実際にアルゴリズムを実行する代わりに、出力分布の形状を計算するだけのオブジェクト空間を作成できます(RNG入力が均一であると仮定)。もちろん、このためには、このようなツールを構築し、アルゴリズムをPyPyまたはその他のツールでビルドする必要があります。この場合、コンパイラを大幅に修正し、それを使用してコードを分析します。


3

単体テストでは、ランダムジェネレーターを、すべてのコーナーケースをカバーする予測可能な結果生成するクラスに置き換えます。つまり、疑似ランダマイザーが可能な限り低い値と可能な限り高い値を生成し、同じ結果が数回連続して生成されることを確認してください。

Random.nextInt(1000)が0または999を返す場合に発生するオフバイワンのバグなど、ユニットテストで見落とさないようにする必要があります。


3

Sevcikova et al:「確率システムの自動テスト:統計的に接地されたアプローチ」(PDF)をご覧ください。

この手法は、UrbanSimシミュレーションプラットフォームのさまざまなテストケースに実装されています。


それは良いことです。
キース

2

簡単なヒストグラムアプローチは良い最初のステップですが、ランダム性を証明するには十分ではありません。均一なPRNGの場合、(少なくとも)2次元の散布図も生成します(xは以前の値で、yは新しい値です)。このプロットも均一でなければなりません。システムには意図的な非線形性があるため、これはあなたの状況では複雑です。

私のアプローチは:

  1. ソースPRNGが十分にランダムであることを検証します(または与えられたものとします)(標準的な統計的尺度を使用して)
  2. 制約のないPRNGからdatetimeへの変換が出力空間で十分にランダムであることを確認します(これにより、変換にバイアスがないことを確認します)。ここでは、簡単な1次均一性テストで十分です。
  3. 制約されたケースが十分に均一であることを確認します(有効なビンに対する単純な1次均一性テスト)。

これらのテストはそれぞれ統計的であり、高い信頼度で誤検知と誤検知を回避するために多数のサンプルポイントが必要です。

変換/制約アルゴリズムの性質に関して:

指定:擬似乱数値pを生成する方法(0 <= p <= M)

必要:出力yは(おそらく不連続な)範囲0 <= y <= N <= M

アルゴリズム:

  1. r = floor(M / N)つまり、入力範囲内に収まる完全な出力範囲の数を計算します。
  2. pの最大許容値を計算します。p_max = r * N
  3. 以下の値が見つかるまでpの値を生成しますp_max
  4. 計算する y = p / r
  5. yが受け入れ可能な場合はそれを返し、そうでない場合はステップ3で繰り返します

重要なのは、不均一に折り畳むのではなく、許容できない値を破棄することです。

擬似コードで:

# assume prng generates non-negative values
def randomInRange(min, max, prng):
    range = max - min
    factor = prng.max / range

    do:
        value = prng()
    while value > range * factor
    return (value / factor) + min

def constrainedRandom(constraint, prng):
    do:
        value = randomInRange(constraint.min, constraint.max, prng)
    while not constraint.is_acceptable(value)

1

コードが失敗しないこと、または適切な場所で適切な例外をスローすることを検証することとは別に、有効な入力/応答のペアを作成し(これを手動で計算することもできます)、テストで入力をフィードし、期待される応答を返すことを確認できます。素晴らしいことではありませんが、私ができることはそれだけです。ただし、あなたの場合、それは本当にランダムではありません。スケジュールを作成したら、ルールの適合性をテストできます。週に3回、9〜9の検査が必要です。検査が行われた正確な時間をテストするための本当の必要性や能力はありません。


1

何度も実行して、希望するディストリビューションが得られるかどうかを確認するよりも良い方法はありません。50の潜在的な検査スケジュールが許可されている場合、テストを500回実行し、各スケジュールが10回近く使用されることを確認します。ランダムジェネレーターシードを制御して確定性を高めることができますが、これにより、テストが実装の詳細とより緊密に結合されます。


しかし、それが本当にランダムな場合、時折、一部のスケジュールがまったく使用されません。また、場合によっては、一部のスケジュールが20回以上使用されることもあります。各スケジュールが「10回近く」使用されていることをどのようにテストするつもりかはわかりませんが、ここでテストする条件に関係なく、プログラムが仕様どおりに機能しているときにテストが失敗することがあります。
ダウッドは、モニカを復活させると

@DawoodibnKareemで十分なサンプルサイズ(および均一性の合理的な制限)を使用すると、テストが10億分の1に失敗する可能性を減らすことができます。そして、一般的にそのような統計はnで指数関数的であるので、それらの数値に到達するのにあなたが期待するよりも少ないです。
mbrig

1

具体的な定義のない曖昧な状態をテストすることはできません。生成された日付がすべてのテストに合格した場合、理論的にはアプリケーションは正しく機能しています。コンピュータは、そのようなテストの基準を確認できないため、日付が「十分にランダム」であるかどうかを通知できません。すべてのテストに合格しても、アプリケーションの動作がまだ適切でない場合、テストカバレッジは経験的に不十分です(TDDの観点から)。

私の見解では、分布が人間の匂いテストに合格するように、任意の日付生成制約を実装することが最善です。


2
自動テストによりランダム性を絶対に決定できます。十分な数のサンプルを生成し、ランダム性の標準テストを適用してシステムのバイアスを検出するだけです。これはかなり標準的な学部生のプログラミング演習です。
フランクシュツァルバ

0

ランダマイザーの出力を記録するだけです(疑似、量子/カオス、または実世界)。次に、ユニットテストケースを作成するときに、テスト要件に合った、または潜在的な問題やバグを明らかにする「ランダム」シーケンスを保存して再生します。


0

このケースは、プロパティベースのテストに理想的なようです。

簡単に言えば、テストフレームワークがテスト対象コードの入力を生成し、テストアサーションが出力のプロパティを検証するテストモードです。フレームワークは、テスト中のコードを「攻撃」し、エラーに追い詰めるのに十分なほどスマートになります。フレームワークは通常、乱数ジェネレータシードをハイジャックするのに十分なほどスマートです。通常、最大N個のテストケースを生成するか、最大N秒実行するようにフレームワークを構成し、前回の実行で失敗したテストケースを記憶し、最初に新しいコードバージョンに対してこれらを再実行できます。これにより、開発中の高速反復サイクルと、帯域外/ CIでの低速で包括的なテストが可能になります。

以下は、sum機能をテストする(ダム、失敗)例です。

@given(lists(floats()))
def test_sum(alist):
    result = sum(alist)
    assert isinstance(result, float)
    assert result > 0
  • テストフレームワークは一度に1つのリストを生成します
  • リストの内容は浮動小数点数になります
  • sum が呼び出され、結果のプロパティが検証されます
  • 結果は常にフロートです
  • 結果は正です

このテストでは、「バグ」の束を見つけますsum(これらのすべてを自分で推測できた場合はコメントします)。

  • sum([]) is 0 (フロートではなくint)
  • sum([-0.9]) 負です
  • sum([0.0]) 厳密に正ではない
  • sum([..., nan]) is nan ポジティブではない

デフォルト設定では、hpythesis「悪い」入力が1つ見つかった後にテストを中止します。これはTDDに適しています。多くの/すべての「悪い」入力を報告するように構成することは可能だと思っていましたが、今はそのオプションが見つかりません。

OPの場合、検証されるプロパティはより複雑になります。検査タイプAが存在し、検査タイプAが1週間に3回、検査時間Bが常に午後12時、検査タイプCが9から9まで[指定されたスケジュールは1週間] A、B、Cなどすべて存在

最も有名なライブラリはHaskellのQuickCheckです。他の言語のライブラリのリストについては、以下のウィキペディアのページを参照してください。

https://en.wikipedia.org/wiki/QuickCheck

仮説(Python用)には、この種のテストに関する優れた記事があります。

https://hypothesis.works/articles/what-is-property-based-testing/


-1

ユニットテストできるのは、ランダムな日付が有効かどうか、または別のランダムな日付を選択する必要があるかどうかを判断するロジックです。

大量の日付を取得し、それらが適切にランダムかどうかを判断する以外に、ランダムな日付ジェネレーターをテストする方法はありません。


-1

目標は、単体テストを作成して合格することではなく、プログラムが要件に適合することを確認することです。これを行う唯一の方法は、最初に要件を正確に定義することです。たとえば、「ランダムな時間に週3回の検査」に言及しました。要件は次のとおりです:(a)3回の検査(2または4ではない)、(b)予期せずに検査したくない人が予測できないとき、および(c)近すぎない- 5分間隔の2つの検査はおそらく無意味であり、あまり離れていないかもしれません。

ですから、私よりも正確に要件を書き留めてください。(a)と(c)は簡単です。(b)の場合、次の検査を予測し、単体テストに合格するために、できる限り巧妙なコードを記述できます。そのコードは、純粋な推測よりも良く予測できてはなりません。

そしてもちろん、検査が本当にランダムである場合、予測アルゴリズムまったくの偶然で正しい可能性があることに注意する必要があります。さらにいくつかのテストを実行する可能性があります。結局のところ、カウントするのは検査スケジュールであり、それがどのように作成されたかは関係ないため、乱数ジェネレーターをテストすることはありません。


いいえ、ただ。単体テストは、プログラムがその要件に適合することを証明するため、2つはまったく同じです。そして、私はランダムなアルゴリズムをリバースエンジニアリングするための予測ソフトウェアを書く仕事をしていません。もし私がそれについてあなたに話さないなら、私は彼らの鍵を予測し、最高入札者に秘密を売ることによって、安全なウェブサイトを破壊することを殺しているでしょう。私のビジネスは、制約内ではあるが制約内で予測不可能な時間を作成するスケジューラーを作成しています。
キース14年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.