「全網羅テスト」という言葉について 〜または、PICT活用時の落とし穴〜 #テストアドカレ

はじめに

本記事はソフトウェアテスト Advent Calendar 2020 20日目の記事です。

TL;DR

  • PICTで作成するテストケースは、特定条件下の元で網羅したテストである
  • 「PICTによって全網羅したテストを作っている」と言うのは誤解を与えるのでやめておいた方が良い
  • PICTを使う最適なタイミングが存在するので、PICTを毎回使うのではなく別の組み合わせテスト技法や単体テストの活用も検討しよう

目次

「全網羅してます」

私は「全網羅してます」という発言を聞くと気になってしまいます。もう少し細かく言うと、網羅基準がハッキリしていない状態で「全網羅しました」と言われるとツッコミを入れることがあります*1

今回は、具体的な例を元に、全網羅について考えてみます。

今回の例

Aさんは有効桁数が9桁の電卓アプリを作りました。ですが、今回の電卓アプリのテストを全くやってないことに気づきました。そこで、全網羅するような組み合わせテストを考えることにしました。

今回の組み合わせテストのやり方

Aさんは今回の組み合わせテストを

(入力数値X)(計算記号)(入力数値Y)

という3つの組み合わせで考えることにしました。

例えば、入力数値Xが1,計算記号が+,入力数値Yが2だったとすると、

1+2

となり、期待値は3となります。

PICTを使用する

この組み合わせテストを行うにあたって、AさんはPICTというツールの存在を知りました。候補の値を設定すると、勝手に組み合わせを作ってくれるらしいです。

github.com

またPICTと同様に、Excelを用いて組み合わせテストを作ってくれる、PICT Masterというツールもあるみたいです。

ja.osdn.net

PICTに設定する値

Aさんは、PICTに下記の値を候補として設定しました。

入力数値Xの候補 計算記号の候補 入力数値Yの候補
0 + 1
100 - 2
123456789 × -123456789
987654321 ÷

PICTで作成されたテストケース

上記の値を設定をして、PICTを実行することで、下記のテストケースが作成できました。

入力数値X 計算記号 入力数値Y
987654321 × 1
123456789 - 2
100 ÷ 2
0 × -123456789
987654321 ÷ -123456789
123456789 + -123456789
0 - 1
100 - -123456789
987654321 + 2
987654321 - 2
0 ÷ 2
100 + 1
123456789 ÷ 1
123456789 × 2
0 + 1
100 × 2

これらのテストケースを全て作成することで、Aさんは全網羅テストをすることができました。

めでたしめでたし…?

今回の例の検証

今回の例は本当にこれで良かったのでしょうか?少しふりかえりながら考えてみます。

作成したテストケースを考える

作成したテストケースを、期待値も含めて記載してみましょう。

入力数値X 計算記号 入力数値Y 期待値
987654321 × 1 987654321
123456789 - 2 123456787
100 ÷ 2 50
0 × -123456789 0
987654321 ÷ -123456789 -8.000000073
123456789 + -123456789 0
0 - 1 -1
100 - -123456789 123456889
987654321 + 2 987654323
987654321 - 2 987654319
0 ÷ 2 0
100 + 1 101
123456789 ÷ 1 123456789
123456789 × 2 246913578
0 + 1 1
100 × 2 200

気になる内容

勘の良い人は気付いたかもしれません。実はAさんが今回作成した組み合わせテストでは、気にしたい下記の2つの内容がテストケースに含まれていません。

  • 桁数オーバーの例(999999999+1など)
  • ゼロ除算の例(1÷0など)

どうしてこの2つの内容が含まれていないのでしょう?

それは、「PICTを用いて全網羅をした」と考えていたことに対して、2つの落とし穴にハマっているからです。

全網羅の落とし穴その1:網羅基準

(デフォルトの設定で)PICTで作成されるテストケースは、全組み合わせの網羅ではなく、二因子間網羅です。

因子とは、テストの組み合わせを考えるときの項目名をいいます。今回の場合は「入力数値X」「計算記号」「入力数値Y」です。

一方、1つの因子に対しての具体的な値を水準と言います。今回の場合、因子が「入力数値X」の水準は「0」「100」「123456789」「987654321」となります。

例えば、今回の例の場合、入力数値X、計算記号、入力数値Yの3つの因子を組み合わせたパターンは下記の通りになります。

入力数値X 計算記号 入力数値Y 期待値
0 + 1 1
0 + 2 2
0 + -123456789 -123456789
0 - 1 -1
0 - 2 -2
0 - -123456789 123456789
0 × 1 0
0 × 2 0
0 × -123456789 0
0 ÷ 1 0
0 ÷ 2 0
0 ÷ -123456789 0
100 + 1 101
100 + 2 102
100 + -123456789 -123456689
100 - 1 99
100 - 2 98
100 - -123456789 123456889
100 × 1 100
100 × 2 200
100 × -123456789 桁数エラー
100 ÷ 1 100
100 ÷ 2 50
100 ÷ -123456789 -8.1E-07
123456789 + 1 -8.10000007
123456789 + 2 123456791
123456789 + -123456789 0
123456789 - 1 123456788
123456789 - 2 123456787
123456789 - -123456789 246913578
123456789 × 1 123456789
123456789 × 2 246913578
123456789 × -123456789 桁数エラー
123456789 ÷ 1 123456789
123456789 ÷ 2 61728394.5
123456789 ÷ -123456789 -1
987654321 + 1 987654322
987654321 + 2 987654323
987654321 + -123456789 864197532
987654321 - 1 987654320
987654321 - 2 987654319
987654321 - -123456789 1111111110
987654321 × 1 987654321
987654321 × 2 1975308642
987654321 × -123456789 桁数エラー
987654321 ÷ 1 987654321
987654321 ÷ 2 493827160.5
987654321 ÷ -123456789 -8.000000073

このように、3つを組み合わせたパターンの網羅(三因子間網羅)をすると、桁数エラーの組み合わせがあることが分かります。

一方で、PICTが(デフォルトの設定で)行うのは二因子間網羅です*2

二因子間網羅とは、2つの因子の間であれば全ての水準を網羅していることを示しています。

例えば、今回PICTで作成したテストケースのうち、入力数値Xが0であるテストケースを抜き出してみます。

入力数値X 計算記号 入力数値Y 期待値
0 × -123456789 0
0 - 1 -1
0 ÷ 2 0
0 + 1 1

まず、計算記号に着目すると、「+」「-」「×」「÷」の全ての水準が登場していることが分かります(青字部分)。つまり、因子「入力数値X」の水準「0」に対して、因子「計算記号」の水準全てと組み合わせていることになります。

次に、入力数値Yに着目すると、入力数値Yに設定することにしていた「1」「2」「-123456789」の全ての水準が登場していることが分かります(緑字部分)。つまり、因子「入力数値X」の水準「0」に対して、因子「入力数値Y」の水準全てと組み合わせていることになります。

他の入力数値Xで設定している「100」「123456789」「987654321」も同様に調査すると、計算記号と入力数値Yで設定している値それぞれが少なくとも1回以上登場していることが分かります。

入力数値が「100」の場合

入力数値X 計算記号 入力数値Y 期待値
100 ÷ 2 50
100 - -123456789 123456889
100 + 1 101
100 × 2 200

入力数値が「123456789」の場合

入力数値X 計算記号 入力数値Y 期待値
123456789 - 2 123456787
123456789 + -123456789 0
123456789 ÷ 1 123456789
123456789 × 2 246913578

入力数値が「987654321」の場合

入力数値X 計算記号 入力数値Y 期待値
987654321 × 1 987654321
987654321 ÷ -123456789 -8.000000073
987654321 + 2 987654323
987654321 - 2 987654319

つまり、因子「入力数値X」因子「計算記号」の網羅や、因子「入力数値X」因子「入力数値Y」の網羅ができているということです。

このように、PICTで作成される二因子間網羅では、2つの因子の間では全ての水準が登場していることになります。

ただし、下記の例のように、三因子の組み合わせによって初めて分かるようなテストが、二因子間網羅のテストケースに入る保証はありません*3

入力数値X 計算記号 入力数値Y 期待値
100 × -123456789 桁数エラー
123456789 × -123456789 桁数エラー
987654321 × -123456789 桁数エラー

つまり、「PICTを使って作成されたテストを全網羅したテストケース」が、すなわち優れたテストケースとは限りません。

全網羅の落とし穴その2:設定値

PICTでは、自分が設定した因子や水準を元にテストケースが作成されます。つまり、作成されるテストケースは自分が設定した因子や水準に依存しています。

今回の場合、因子「入力数値Y」に水準「0」がありません。この状態でPICTでテストケースを作成しても、絶対に「(入力数値X)÷0」が発生しません。

このように、自分が設定した因子や水準が適切でないと、二因子間網羅や三因子間網羅をしても確認したいテストが作れなくなります*4

落とし穴にハマらないために:PICTの使うタイミングについて

「組み合わせテストならばPICT(PICT Master)を使えば良い」というイメージが付きがちですが、PICTを使うタイミングはきちんと考えた方が良いです。

すぐにPICTを用いるのではなく、下記2点に注目しましょう。

  • そのテストは本当に組み合わせて考えるべきなのか
  • その組み合わせは無則な組み合わせになっているのか

注目点1:そのテストは本当に組み合わせて考えるべきなのか

今回の例はそもそも組み合わせテストが必要なのでしょうか。

例えば、「ゼロ除算や桁数エラーは組み合わせテストで行うのではなく、単体のテストで行うべき」という意見があるでしょう。私もこの意見に賛成です。*5

例えば、「1÷0」のようなテストは単体テストで行うことで、組み合わせテストではゼロ除算のテストが無くても良いでしょう。

なお、「ゼロ除算のテストはテストコードで行なっているから、画面単体のテストは実施しない」という考え方については少し反対です。

テストコードで行なっているゼロ除算のテストは「ゼロ除算の返り値(Exception)が返ってくる」ことを確認しているはずです。

それとは別に「ゼロ除算の返り値(Exception)が来た場合には、その旨を画面に表示する」というテストを行うべきでしょう。ただし、これを組み合わせテストで行う必要はない(単体テストで実施すれば良い)と思っています*6

注目点2:その組み合わせは無則な組み合わせになっているのか

PICTは無則な組み合わせテストの時に使う方が良いです。

無則な組み合わせというのは、因子間でのどの組み合わせによっても期待値に影響がないものを指します。

一方、因子間の組み合わせによって期待値に影響があるものを有則な組み合わせと言います。

例えば今回の場合、因子「計算記号」の水準「÷」と因子「入力数値Y」の水準「0」を組み合わせると、エラー(ゼロ除算)が発生するので、有則な組み合わせと言えます。

無則な組み合わせの例としては、因子「入力数値」と因子「画面の明るさ調整」などです。

このような無則な組み合わせの時に初めてPICTを検討しましょう*7*8

有則な組み合わせのテストはどのようにすれば良いのか

それでは、今回のように有則な組み合わせの場合にはどのようにテストを考えれば良いのでしょうか?

PICTを活用した二因子間網羅ではなく、デシジョンテーブル・CFD・原因結果グラフといったテスト技法の活用を検討すると良いでしょう。

これらの技法の説明については、WACATEの過去資料に素晴らしいスライドがあったので、そちらを参考にしてください。*9

デシジョンテーブル

www2.slideshare.net

CFD

www2.slideshare.net

原因結果グラフ

www2.slideshare.net

デシジョンテーブルを活用する際に、全ての因子の組み合わせだと組み合わせ爆発が起きてしまう。…ということについては、nemorineさんの記事などを参考にしてください。

nemorine.hateblo.jp

おわりに:PICTで作成した全網羅なテストとは

結局、PICTで作成したテストケースは全網羅をしていないのでしょうか?いいえ、ある条件下の元で全網羅しているテストです。

PICTで今回作成したテストケースというのは、「自ら設定した因子と水準」「二因子間網羅という網羅基準」において全網羅しているテストケースといえます。*10

この考えを抜きにして、「PICTによって全網羅したテストを作っています」と言うのは誤解を与えるのでやめておいた方が良いでしょう。

PICTは適切なタイミングで使うことで、効果的にテストケースを削減できます。落とし穴にハマらないようにしつつPICTを使っていきましょう!

*1:思えば昨年は「機能テスト」という言葉が気になって記事を書いてましたね…。nihonbuson.hatenadiary.jp

*2:PICT実行時にオプションを指定することで、三因子間網羅のテストを作成することは可能です。しかし、当然ですが二因子間網羅の場合よりもテストケース数は格段に増えます。

*3:絶対に入らない訳ではなく、入る可能性もあります

*4:もしも、私がAさんの作業内容を見たら、「どうしてPICTを使って組み合わせテストを行おうと考えたのか」「どうして入力数値Xの水準が『0』『100』『123456789』『987654321』で入力数値Yの水準が『1』『2』『-123456789』なのか」について質問し、「(もしもこのまま組み合わせテストを行う場合)『0』や『999999999』を入力数値Yの水準に加える」ことを提案することになるでしょう。

*5:今回は、前提として

今回の電卓アプリのテストを全くやってないことに気づきました。

と書いているため、ゼロ除算のテストがないまま組み合わせテストを終わらせてリリースを迎えるのはどうかと思い、組み合わせテストの中身について指摘しています。本来ならば、「そもそも組み合わせテストではない方が良いのでは?」と提案します。

*6:テストコードではゼロ除算のテストを境界値分析を用いてテストしている場合は、画面表示のテストに関しては境界値のテストを行わず、同値分割でのテストケースにするかもしれません。

*7:そもそも、因子「入力数値」と因子「画面の明るさ調整」で組み合わせのテストをやるべきなのかについては検討する必要があるでしょう。

*8:無則な組み合わせテストを活用している技法の1つにHAYST法(HAYST法ツール)があります。HAYST法では、一見すると関係なさそうな因子水準を組み合わせることで、思いもよらないバグが見つかることを狙いにしています。なのでHAYST法を使う前提として、ある程度のテストを行なっており、それでもバグが見つからない時に最後に行うものだという理解です。ほとんどテストしてない時点で有則な因子同士を用いてHAYST法を使うことは、本来の使い方とは違うのかなと思っています。(それによってバグは見つかるかもしれないが、本来のHAYST法の狙いではないという認識)

*9:「(調べたいテスト技法) WACATE」と入力してググることで、良質な資料を見つけることができるという知見を得ました。

*10:ここまで書いて、「全網羅」という言葉には、「全ての組み合わせを網羅している」という意味で使う場合と、「特定条件下の元での組み合わせパターンを全て網羅している」という意味で使う場合の2つあることに気付きました。「特定条件下の元で」を意識しないと、この2つの意味合いの違いが無い状態で言葉を使うことになりそうです。