while を単体テストする
興味深いポストを見た。曰く:
while 文を「単体テスト」することはできるか。できるならどのように行うか。while 文は条件式 b と文 S を使ってたとえば while b do S のようになる。この文をテストするのではなく、b や S を除いた while という「単体」の働きをテストする。
while 文を「単体テスト」することはできるか。できるならどのように行うか。while 文は条件式 b と文 S を使ってたとえば while b do S のようになる。この文をテストするのではなく、b や S を除いた while という「単体」の働きをテストする。
— hatsugai ∈ PRINCIPIA (@hatsugai) April 1, 2024
単体テストでテストできる対象は「評価できる値」で「比較できるもの」となるため、 while そのものをテストすることは難しい。(単体テストが `Assert.Equals` 的な(結果の)値の比較をするツールだという前提で)
while 文の while は、単体で値を持つものではないからである。
ただ While 関数を実装することで間接的に確かめることはできそう。ざっくりとこんな感じ:
While(b, S): void { while (b) S; }
(一般的な)手続き型の言語だと b や S は呼び出し時に評価されてしまうため、 While 関数内で(必要に応じて何度も)呼び返せるような関数である必要があるだろう。つまり:
While(b: void -> bool, S: void -> void): void { while (b()) S(); }
ここまで準備できれば、あとはテストケースを考えるターン。
評価するたびに bool の値を返す関数 b と、呼びだすことができる(値を戻さない)関数 S とのふたつを受けとり、 b の評価結果が偽になるまで S を呼びだすこと。
単体テストの文脈でなら、以下3つくらいのケースを作るだろうか。
b を、偽を返す関数で与えたときに S が呼び返されないこと
b を、真、続いて偽を返す関数で与えたときに S が一度だけ呼び返されること
b を、真、真、偽と返す関数で与えたときに S が二度呼び返されること
ただし(単体テストが常にそうであるように)対象の完全な性質はテストで確認できない。今回で言えば b の評価結果が「偽になるまで S を呼びだすこと」は確かめられない。(偽にならない b を与えるとテストが停止しなくなる)
先の3つのテストケースは(疑似コードだけれど)、わたしなら次のように実装するだろう。
While(b: void -> bool, S: void -> void): void { while (b()) S(); }
test_While_returns_control_without_calling_S_if_b_returns_false(): void {
var count = 0;
var b = () => false;
var S = () => count += 1;
While(b, S);
Assert.True(count == 0);
}
test_While_calls_S_once_if_b_yields_true_and_then_false(): void {
var count = 0;
var b = () { yield true; yield false; }
var S = () => count += 1;
While(b, S);
Assert.True(count == 1);
}
test_While_calls_S_two_times_if_b_yields_true_true_and_then_false(): void {
var count = 0;
var b = () { yield true; yield true; yield false; }
var S = () => count += 1;
While(b, S);
Assert.True(count == 2);
}