配列の要素をランダムにシャッフルするメソッドを考えてみましょう。これが動作していることを確認するための、シンプルかつ堅牢なユニットテストをどのように書きますか?
2つのアイデアを思いつきましたが、どちらも目立った欠点があります。
サイコロの目をシミュレートして乱数を返す2つ目の関数を考えてみましょう。この関数はどのようにテストするのでしょうか。この関数が乱数を返すかどうか、どのようにテストするのでしょうか。
これらの例だけでなく、一般的なコードのランダムな要素をテストするための洞察を提供する答えを探しているのです。ユニットテストは正しい解決策なのでしょうか?そうでない場合、どのような種類のテストがありますか?
皆を安心させるために、私は自分の乱数発生器を書いているわけでは ** ありません。
最初の質問に対しては、アルゴリズムの結果を知っている乱数列を与える偽のクラスを作ります。そうすることで、ランダム関数の上に構築したアルゴリズムが機能することを確認することができます。つまり、次のようなものです。
Random r = new RandomStub([1,3,5,3,1,2]);
r.random(); //returns 1
r.random(); //returns 3
...
ユニットテストに、複数回実行してその結果を保証するテストを追加する必要があります。
2
が出てくるはずです。元のソートで配列がソートされる頻度はどれくらいでしょうか?数百回ソートして、x%だけソートが変わらないと断言してください。
これは実はすでに統合テストであり、アルゴリズムとランダム関数を一緒にテストしているのです。いったん本物のランダム関数を使ったら、もう単一のテスト実行では済まされません。
経験上(私は遺伝的アルゴリズムを書きました)、アルゴリズムの単体テスト、ランダム関数の分布テスト、統合テストを組み合わせることが望ましいと思います。
これには2つのパートがあります。無作為化のテストと無作為化を使用するもののテストです。
無作為化のテストは比較的簡単です。乱数生成器の周期が期待通りであること(いくつかのちょっと乱暴な種を使ったいくつかのサンプルについて、ある閾値の範囲内)、そして大きなサンプルサイズでの出力の分布が期待通りであること(ある閾値の範囲内)をチェックするのです。
ランダム化を利用したテストは、決定論的な擬似乱数発生器を用いて行うのが最適です。乱数の出力はシード(入力)に基づいて知られているので、入力と期待される出力に基づいて通常のユニットテストを行うことができます。もしあなたの RNG が決定論的でないなら、決定論的な (あるいは単にランダムでない) RNG でモックを作ってください。ランダム化のテストは、それを消費するコードから分離して行います。