ミューテーション解析およびミューテーションスコアは過去の記事でも触れているのであわせてご確認ください。
テストの品質をどのように考えるか
ところで、読者の皆さんはこのような言葉を知っていますか?
”Program testing can be used to show the presence of bugs, but never to show their absence!” (プログラムテストによって誤りの存在を示すことはできる。しかし、誤りが存在しないことを示すことはできない。)
こちらはエドガー・ダイクストラの言葉です。さまざまな場面で引用されていますが、有名な書籍「構造化プログラミング」にも記述があります。
ソフトウェア開発において、品質を確保するためソフトウェア・テストは必要不可欠です。しかし、多くの時間をかけてテストをしたらからといって、そのソフトウェアにバグがないとは言い切れません。皆さんはソフトウェア・テストにおいて品質を十分に担保したと、どのように判断していますか? そもそもソフトウェア・テスト自体の品質(そのテストは妥当であるか、無駄なテストではないか)はどのように担保していますか?
テストケースが多ければ多いほどいいという考え方もありますが、そうなるとテストケースの開発や実施に過剰なコストがかかっているかもしれません。最小のコストで、プログラムの誤りが発見できる方法は、誰もが知りたいでしょう。本記事の手法は、その問いにいくらか回答を示せるはずです。
組み合わせテスト技法での課題とは
コードのテストを行う時、特定のコードに対して入力(パラメータ)を与える必要があります。複数のパラメータがあるとしたら、考えられる値を全部試すことが考えられます。このような複数のパラメータがあるテストは「組み合わせテスト」と呼ばれます。もちろん全部の組み合わせをテストとすると、効率は良くありません。そこで、組み合わせ数をなんらかの方法で減少させつつ、テストの品質を確保する組み合わせテストが求められます。代表的な技法として「直交表」や「オールペア法」が有名です。
これらの技法はパラメータ(因子)とパラメータの値(水準)の組み合わせを合理的に絞り込む技法です。この技法はソフトウェアの単体テストにおけるブラックボックステストのテストケース作成にも有効です。
しかし単体テストに適用した場合、テスト対象関数の引数や構造体のメンバ変数、グローバル変数など、事前条件となる因子は2個や3個ではありません。複雑な関数の場合、10個や20個にもなります。たとえ、ありえない組み合わせ(禁則)を除いたとしても、すべての因子を組み合わせた場合、組み合わせは膨大な量になります。
表1は、因子と水準を想定した際に、禁則がない場合、どれくらいのテストケースがあるかを示したものです。因子が6の行は、水準が2の因子が4つ、水準が3と4の因子が1つあるので、あらゆる組み合わせを考えれば、「2×2×2×2×3×4=192」となり、これが全通りの数です。
因子数が18の場合は水準2が8個、水準3が5個、水準4が5個であるとすれば、すべての組み合わせは「28×35×45」で約6370万通りと、膨大な数になります。因子として、関数の引数だけではなく、フラグのような変数も考慮しないといけなくなると、因子数は思った以上に増えるかもしれません。「2因子間」以降は、この後で説明します。

ソフトウェアにおける単体テストで、ひとつの関数に対して6370万個のテストケースを実行するのは、あまりにも非現実的です。そこで、この関数に対してオールペア法を適用してみます。この方法だと、因子の組み合わせ数が2の場合、2つの因子について見た際に取り得るすべての組み合わせが存在するようにする、というルールでパラメータを決めます。
テストケースをオールペア法に基づき減少させる
表1の6因子の場合、水準2の因子をA~D、水準3の因子をE、水準4の因子をF、水準を1、2……のように示すと、(A,B)については{(1,1), (1,2), (2,1), (2,2)}のように、まず4通り作れます。ここで、(A,B,C)については{(1,1,1), (1,2,1), (2,1,2), (2,2,2), (2,*,1), (1,*,2)}の6通りを考えた上で、AとB、BとC、AとCの各組み合わせにだけに注目すれば、水準のすべての組み合わせが登場します(*は1でも2でも構いません)。
同様にして6つの因子に拡張すると、表1の「2因子間」に示す通り、12通りの水準の組み合わせで要件を満たすことになります。表2はその様子を表しました。A~Fの行は、水準を示しています。2列目以降の各列が、水準の組み合わせを表しており、空欄は任意の水準であることを示します。A~Bの行は、AとBの水準を組み合わせた際、○のある列によって、すべての組み合わせが存在することを示しています。すべての組み合わせに対して検証しています。
A | 1 | 1 | 2 | 2 | 2 | 1 | 2 | 1 | 1 | |||
B | 1 | 2 | 1 | 2 | 2 | 1 | 1 | 2 | 2 | 1 | ||
C | 1 | 1 | 2 | 2 | 1 | 2 | 2 | 1 | 1 | 2 | ||
D | 1 | 2 | 1 | 2 | 1 | 2 | 1 | 2 | 2 | 1 | ||
E | 1 | 2 | 3 | 1 | 2 | 3 | 2 | 3 | 1 | 2 | 3 | 1 |
F | 1 | 2 | 3 | 4 | 1 | 2 | 3 | 4 | 3 | 4 | 1 | 2 |
A-B | ○ | ○ | ○ | ○ | ||||||||
A-C | ○ | ○ | ○ | ○ | ||||||||
B-C | ○ | ○ | ○ | ○ | ||||||||
A-D | ○ | ○ | ○ | ○ | ||||||||
B-D | ○ | ○ | ○ | ○ | ||||||||
C-D | ○ | ○ | ○ | ○ | ||||||||
A-E | ○ | ○ | ○ | ○ | ○ | ○ | ||||||
B-E | ○ | ○ | ○ | ○ | ○ | ○ | ||||||
C-E | ○ | ○ | ○ | ○ | ○ | ○ | ||||||
D-E | ○ | ○ | ○ | ○ | ○ | ○ | ||||||
A-F | ○ | ○ | ○ | ○ | ○ | ○ | ○ | ○ | ||||
B-F | ○ | ○ | ○ | ○ | ○ | ○ | ○ | ○ | ||||
C-F | ○ | ○ | ○ | ○ | ○ | ○ | ○ | ○ | ||||
D-F | ○ | ○ | ○ | ○ | ○ | ○ | ○ | ○ | ||||
E-F | ○ | ○ | ○ | ○ | ○ | ○ | ○ | ○ | ○ | ○ | ○ | ○ |
表1に示すように、因子が18個の場合に適用すると、2因子間の組み合わせで絞り込むとテストケースは24通りになります。しかし、本当に24通りのテストケースだけでこの全組み合わせが6370万通りもある複雑な関数を十分にテストしたといえるのでしょうか?
組み合わせる因子数を極力絞り込み、なおかつテストが不十分にならないよう、組み合わせるべき因子数をいくつかに設定してテストケースを生成するか判断する必要があります。この組み合わせるべき因子数はどのようにして判断するべきでしょうか?
最適な因子数をどのようにして判断すればいいか?
そこで、オールペア法で生成したテストケースに対して、以下2種類のアプローチで組み合わせるべき因子数を摘出する方法を検証しました。
- コードカバレッジを用いて、オールペア法で作成したテストケースの網羅性を測定
- ミューテーションスコアを用いて、オールペア法で作成したテストケースの品質を測定
この2つが収束する因子数が、最も少ないテストケース数で、コードの網羅性、テスト自体の品質が高い最少のテストケース数であると判断できると考えました。