私は最近、加重平均アルゴリズムを使用して画像を回転させるJSスライドショーに取り組んでいました。ありがたいことに、timgilbertは 加重リストスクリプト を記述しており、これは私が必要とする正確なアルゴリズムを実装しています。しかし、彼のドキュメントでは彼はtodos: "unit tests!"の下で言及しています。
私が知りたいのは、アルゴリズムを単体テストする方法です。加重平均の場合、ランダム性の要素がある場合、平均が正確であることの証明をどのように作成しますか?
類似のコードサンプルは、私の理解に非常に役立ちます。
ここで使用できるテクニックがいくつかあります。
1)乱数ジェネレーターを既定値を生成するものに置き換え、期待される値が得られることを確認します
old_random = Math.random;
Math.random = function() { return 0.5; };
assert_equals(random_pick(), expected_random_pick);
2)アルゴリズムを何度も実行し、結果を合計する
weights = {0:.1, 2:.5, 6:0.4}
totals = {}
count = 10000;
for(x = 0; x < count; x++)
{
totals[random_pick(weights)] += 1 / count;
}
assert(totals[0] - weights[0] < 0.1)
assert(totals[2] - weights[2] < 0.1)
assert(totals[6] - weights[6] < 0.1)
ここでの考え方は、アルゴリズムを十分な回数実行すると、大きな数の法則により合計が理論的に期待されるものに類似するということです。次に、それを確認します。
単体テストは一般的なケースを証明しません。彼らは、特定の(うまくいけば十分に選択された)ケースがいくつかの期待に応えると主張するだけです。そして、ほとんどの場合それで十分です。
いくつかの例を見つけ、手動で計算して結果を確認し、正しいと思われる場合はアサーション下に置いてください。
例をよく選択してください。 「テスト部門」の場合、8/4、4/2、および6/3をテストすることはあまり意味がありません。 8/4のみをテストする方がはるかに優れていますが、8/3(= 2または2.66?)、8 /(-2)、8/0、0/8、0/0、2/8、...コーナーケースを含みます。
期待される結果がすべてです。数値が特定の範囲内に戻ることがわかっている場合は、それをテストします。
私はただ例を挙げて、私ができる主張をするだけです。もちろん、私はこれを単なる巨大なテストではなく、複数のテストにします。ユーティリティメソッドに明確な実装があることを前提として、これを主に疑似コードで記述します。
var data = [['a', 10],
['b', 1],
['c', 1],
['d', 5],
['e', 3]];
var wl = new WeightedList(data);
// wl.peek() returns items at random from the list, and does not modify the list.
var result = wl.peek(); // Ex: ['a']
assertContains(keySet(wl), result);
assertLength(1, result);
var result = wl.peek(3); // Ex: ['a', 'c', 'd']
assertContainsAll(keySet(wl), result);
assertLength(3, result);
// wl.pop() returns random items from the list and removes the items it found
var result = wl.pop(2); // Ex: ['a', 'd'], after which wl consists of [ ['b', 1], ['c', 1], ['e', 3] ]
assertIntersectionIsEmpty(result, keySet(wl));
assertLength(2, result);
// wl.Push() adds new data into the set
// note that despite the terms Push and pop, the weighted list has no natural order
wl.Push('f', 6); // wl is now [ ['b', 1], ['c', 1], ['e', 3], ['f', 6] ]
assertLength(4, wl);
// wl.addWeight() will increase the weight of a list item (or decrease it if the user passes a negative number)
wl.addWeight('b', 4); // wl is now [ ['b', 5], ['c', 1], ['e', 3], ['f', 6] ]
// I doubt you can assert much here but no exception
// wl.shuffle() will return the entire list in random order.
result = wl.shuffle(); // Ex: ['b', 'f', 'c', 'e']
assertLength(4, result);
assertContainsAll(keySet(wl), result);
また、重みが大幅に変わらない場合は、いくつかの操作を何度も繰り返し、ある時点でWeightedList内のすべての値を取得したことを表明できます。あなたはそれらが現れる可能性が非常に高いことがあるように十分にそれを繰り返すことを確認する必要があります。私はあなたがテストを何回実行することを期待するかについての非常に高い見積もりをすばやく提案するでしょう(例えば、10年間1日100回)。失敗の確率をその多くの実行の1つにします。確認する必要があるのは、ランダム値の不運によりこのコードが失敗する確率が、バグが原因で失敗する確率と同じであることです。そうすれば、失敗率が大幅に上がることはありません。
まず、アルゴリズムが処理する入力のセットを確認します。入力のさまざまな分類を見つけてください。たとえば、アルゴリズムが数値の平方根を計算するものである場合(例として)、アルゴリズムが処理する入力のkindsを考えます。そのような入力分類の1つは、正の数、0、負の数です。次に、これらの各クラスから入力を受け取り、それを使用するテストを記述します。
これは入力スペースのパーティション分割の例であり、プログラムが処理する必要があるすべての論理的なケースをカバーするのに役立ちます。
この後、コードカバレッジ分析ツールを使用して、まだテストされていないコード(またはケース)を見つけます。次に、コード内の特定のパスをテストする入力を定式化してみます。これはブラックボックステストの例です。