Edited at

組合せバグの要因絞り込み方法


はじめに

原因結果グラフやCFD法を用いて作成した“デシジョンテーブルを使用する有則の組合せテスト”も、因子(パラメータ)と水準(値)を直交表等に割り付けた“マトリクスを使用する無則の組合せテスト”も、どちらの表にもすべての機能を働かせる組合せは、通常は出現しません。しかしながら、すべての機能を働かせる組合せをテストすることはとても重要です。このテストのことを以降では『全部乗せテスト』と呼ぶことにします。

本エントリーでは全部乗せテストの意義と、全部乗せテストを行ってバグが出たときに効率的に不具合要因を絞り込む方法について記載します。


全部乗せはロマン

ここは札幌のラーメン横丁。シンポジウムの終了後、ラーメンを食べようと、ネモリン、うめちゃん、RINA、みっきーの4人が集まりました。

ネモリン「おい、ウメツ。なに頼む?」

うめちゃん「ちょっと待ってくださいヨー。迷っちゃうなー。やっぱり札幌といえば味噌ですよね。

あとは、トッピングをどうするか、、、味玉、ビッグチャーシュー、メンマ、ネギ、コーン、バター、ワカメ、もやし、ほうれん草、焼き海苔、おっ! アスパラもある!! スクランブルエッグは微妙ですが、さすが北の大地ですね。12種類も選べるなんて! どれも100円か、うーん。迷っちゃうなあ。」

RINA「うめちゃん。“トッピング全部乗せ:500円”ってあるよ。お得じゃない?」

うめちゃん「チッチッ! RINAさん、確かに【全部乗せはロマン】ですが、ラーメンどんぶりに全種類を乗せるために一つひとつは“ちょびっと”になってしまうんです。バラで頼めば、それぞれが小皿に乗ってきてボリューミーです。店主のワナなんです。」

※ 何かのスイッチが入ってしまったようだ。

みっきー「なーにがワナなんだか。ボクは“味噌ラーメン・全部乗せ”でお願いします。」

RINA「私も少しずつ色々味見したいから同じので。」

うめちゃん「決めた! 味噌ラーメンに、味玉2つと、ビッグチャーシュー3枚とバターをトッピングで。」(๑•̀ㅁ•́ฅ✧

ネモリン「オーダーお願いします!

味噌ラーメン4つください。そのうち3つは全部乗せで。一つだけは味玉2つと、ビッグチャーシュー3枚とバターをトッピングで。それと、餃子1皿ね!」

RINA「えっえっ。待ってまって。餃子聞いてない。」

ネモリン「RINAさんも餃子頼みます? ここのお店の餃子は8つ来るから、2つずつ分けようかなと。」

RINA「ネモリン、マジ天使。」

うめちゃん「餃子2皿頼んで、4つずつ分けましょう。」

全員「いいね。」

RINA「ところで、このタブレットは何?」

ネモリン「回転寿司にあるのと一緒で、この操作パネルからもオーダーできるんですよ。」

RINA「なるほど。ふむふむ。」

※ RINAは操作パネルを触りだす。

「トッピングはチェックボックス(☑️)で選択する方法なのね。あ、全部乗せをチェックしたらこれまで選んでいたトッピングのチェックが外れた。

さらにっと、この状態で、全部乗せチェックを外しても、、、(全部乗せをチェックする)前には戻らないのかあ……操作ミスで押したら再入力か……。痛いな。あと同じトッピングを複数食べたいときはどうするんだろう???」

みっきー「おやおや。探索的テストですか?」

RINA「えへへ。つい。職業病かなあ。」

うめちゃん「トッピングのテスト設計をするとしたら直交表ですかねー。L16には15列あってトッピングは12種類だからテストできそう。」

RINA「そうなの? うめちゃん詳しいね。でも、直交表だと全部にチェックが入ったテストって出てこないような気がする。」

みっきー「でてこないよ。ほらL16直交表を見て。1が並ぶ行は無いでしょ。直交表のテストでは、チェック無しのデフォルトを0に、チェック付きのONを1にして割り付けるというルールがあるって秋山さんが言ってたから全部にチェックが入ったONが並ぶ行は現れないよ。」

No.
A
B
C
D
E
F
G
H
I
J
K
L
M
N
O

1
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0

2
0
0
0
0
1
1
0
1
1
0
1
1
0
1
1

3
0
0
0
1
0
1
1
0
1
1
0
1
1
0
1

4
0
0
0
1
1
0
1
1
0
1
1
0
1
1
0

5
0
1
1
1
1
0
0
1
1
0
0
0
1
0
1

6
0
1
1
1
0
1
0
0
0
0
1
1
1
1
0

7
0
1
1
0
1
1
1
1
0
1
0
1
0
0
0

8
0
1
1
0
0
0
1
0
1
1
1
0
0
1
1

9
1
0
1
0
1
1
1
0
1
0
0
0
1
1
0

10
1
0
1
0
0
0
1
1
0
0
1
1
1
0
1

11
1
0
1
1
1
0
0
0
0
1
0
1
0
1
1

12
1
0
1
1
0
1
0
1
1
1
1
0
0
0
0

13
1
1
0
1
0
1
1
1
0
0
0
0
0
1
1

14
1
1
0
1
1
0
1
0
1
0
1
1
0
0
0

15
1
1
0
0
0
0
0
1
1
1
0
1
1
1
0

16
1
1
0
0
1
1
0
0
0
1
1
0
1
0
1

ネモリン「みっきーさんって、ドラえもん?」


チェックボックスのテスト

RINA「探索的テスターの名にかけて、リナは全てのトッピングにチェックをつけたテストをしたいです。」(•̀ㅁ•́)✧

みっきー「ボクも。」

うめちゃん「えっ? そんな技法があるのですか?」

みっきー「テスト技法としてはボクも見たことが無いけど、現場ではよくやられていますよ。」

ネモリン「ドリル本に『有効な値(正常系)は複数同時に確認できるが無効な値(異常系)については一回に一つずつしか確認できない』って書いてあるから、正常系は同時に確認できるとは思っていたけど、深く考えてはいなかったな。」

RINA「具体的に考えようよ。全部チェックするってどういうことだろう?」

みっきー「Wordで、同じ文字に[斜体]、[太字]、[下線]、[色]、[傍点]、[ルビ]、[取消線]、、、を同時に設定しちゃうテストかな。」

RINA「はい。リナ、そういうGUIがあったら絶対全部にチェック入れちゃいます。」

みっきー「そうだよね。仮に『全部乗せテスト』って名づけようか。RINAはどうして全部乗せしたいのかな?」

RINA「だって、一つずつのテストは開発者さんがやってると思うけど、全部乗せはしていない気がするから。」

みっきー「それだけ? 開発者がしていないテストなんて山ほどあると思うけど。」

RINA「全部乗せして大丈夫だったら問題ないから。」

みっきー「何の問題がないの?」

RINA「うーん。ひょっとして組合せ?」

みっきー「ボクが答えを知っているわけじゃないよ。RINAがやっていることですよ。」

RINA「そうですね。うーん。うーん。リナは2機能間だけの組合せだけでは不安だから全組合せをしたいです。」

みっきー「確かに、正常系だけの組合せなら結果が出るから全組合せのテストができるね。それだ。」

RINA「あと、負荷もかかるような気がするの。」

みっきー「直感や閃きは大切にしたいよね。」

ネモリン・うめちゃん「全部乗せテスト重要っぽい気がしてきた。」


全部乗せテスト

ここからは普通のブログになります。

全部乗せテストですが、当たり前すぎて筆者はテスト関係の書籍で見たことがありません。ググってもそれらしいものは見つかりませんでした。見つかった人はコメントで教えてくださいね。

本エントリーでは、全部乗せテストを『同時に設定できる正常系の条件をすべてONにした組合せテスト』と定義します。網羅型のテストというよりも検出型のテストとなります。

※ 異常系についても全部組み合わせてしまう人もいらっしゃいますが、異常系の条件を設定すると、“入力値チェックのワーニング”が出て終わってしまうことが多いので、ここでは考えないことにします。

さて、全部乗せテストのメリットですが、RINAさんは「2機能間だけの組合せだけでは不安だから全組合せをしたい」、「負荷もかかるような気がする」と2つのことを言っていました。(もちろん『このエントリーは実在の人物や団体などとは関係ありません』ので、RINAが実在する誰かと似ていると思っても、それはきっと気のせいです。)

「2機能間だけの組合せだけでは不安だから全組合せをしたい」というのは、直交表やオールペアテストを想定してのことだと思います。一般的な直交表は任意の2機能の組合せを保証しますが、3機能以上の組合せの出現は保証しません。(特別な強度3やそれ以上の強度の直交表も作れなくはありませんが、それらは一般的ではありません)

確かに全機能の組合せを確認したいという気持ちは分かります。しかも、チェックボックスのようにONとOFFの2値(有効なのはONのみ)の場合はチェックボックスを全てONにしたテストを実施すればわずか1件のテストで2機能間、3機能間、、、(略)、、、全機能間の組合せテストを実施できますから、大変お得で有効なテストとなります。

次に、「負荷もかかるような気がする」のほうですが、こちらはちょっと注意が必要かもしれません。たくさんの機能を働かせることは一般的にシステムに対する負荷になりますが、機能の実行に並行・並列性がなく、単に指定した複数の機能が順番に実行するだけでは負荷にならないからです。

負荷テストは、機能を確認するテストとは別に、負荷テストのテスト設計を行い、テスト実行時には、「⁠スループット」と「⁠レスポンスタイム」と「⁠リソース使用量」の3つの指標について測定しながら、きちんと負荷がかかっていることを確認する必要があります。


全部乗せテストのバグ要因の絞込み

前章では、全部乗せテストには『わずか1件で全組合せのテストが出来る』というメリットがあるということを確認しました。

しかし、全部乗せテストでバグが見つかったときに、それをデバッグするのは割とやっかいです。

12個のトッピングの全てにチェックを入れて[オーダー]ボタンを押したときに、操作パネルがハングアップしたとします。

トッピングの一つひとつについては開発者によってテストされ問題のないことが確認されているとします。単機能の動作について開発者のユニットテストで確認することは普通ですから。

つまり、味玉:ON、ビッグチャーシュー:ON、メンマ:ON、ネギ:ON、コーン:ON、バター:ON、ワカメ:ON、もやし:ON、ほうれん草:ON、焼き海苔:ON、アスパラ:ON、スクランブルエッグ:ONの12個のチェックボックスのどれかの組合せでハングアップしたと思われます。

これをデバッグするためには、バグの再現条件をもう少し、絞り込みたいものです。そうしないと再現テストが面倒ですし、ソースコードを見る箇所も多くなるからです。

まず、思いつくのは一つずつ怪しいチェックボックスを減らしていく方法です。チェックを外すことでバグが発生しなくなった設定項目が再現条件の一つとなりますから、12個のチェックボックスなら12回のテストでバグの要因であるチェックボックスの組合せを絞り込むことができます。組み合わせている要因の個数が12個程度でしたら、この方法がよいでしょう。

でも、チェックボックスがもっともっとたくさんあったら、再現条件を絞り込むためのテストの数はたくさんになります。

次に、二分探索的な方法を思いつくかもしれません。つまり12個のチェックボックスを6個ずつの2つのグループに分けて、それぞれをテストする方法です。今回の例なら、

グループA
グループB

味玉:ON、ビッグチャーシュー:ON、メンマ:ON、ネギ:ON、コーン:ON、バター:ON、ワカメ:OFF、もやし:OFF、ほうれん草:OFF、焼き海苔:OFF、アスパラ:OFF、スクランブルエッグ:OFF
味玉:OFF、ビッグチャーシュー:OFF、メンマ:OFF、ネギ:OFF、コーン:OFF、バター:OFF、ワカメ:ON、もやし:ON、ほうれん草:ON、焼き海苔:ON、アスパラ:ON、スクランブルエッグ:ON

の2グループにわけて、例えば、グループAでバグが再現したら、12個から、6個(味玉:ON、ビッグチャーシュー:ON、メンマ:ON、ネギ:ON、コーン:ON、バター:ON)へ絞り込めるわけです。

ところが、原因となるチェックボックスが別のグループにある場合、この例で言えば、『味玉:ON×スクランブルエッグ:ON』の組合せがバグの原因だった場合には、グループAとグループBともに、バグは再現しません。

そこで、グループを2つではなく、3つに分けます。下表ではONのみを記載しています。

グループA
グループB
グループC

味玉:ON、ビッグチャーシュー:ON、メンマ:ON、ネギ:ON
コーン:ON、バター:ON、ワカメ:ON、もやし:ON、
ほうれん草:ON、焼き海苔:ON、アスパラ:ON、スクランブルエッグ:ON

まず、グループAとグループBをあわせたテストを行い、ここでバグが再現されたらグループCを外します。バグが再現しなければ『グループB+グループC』、それでも再現しなければ、『グループA+グループC』のテストを行います。

こうすることで、最悪でも1ターン(3回)のテストで怪しいチェックボックスを2/3に減らすことが出来ます。2ターン目も同様です。したがって、5ターン(15回)行えば、(2/3)^5 = 32/243 = 0.13と全体の1/7程度に絞り込めます。要因の数が100個あっても13個に絞り込めるというわけです。

一方で一つずつチェックボックスを減らしていく方法では、15回(5ターン分)のテストでは、100個が85個に絞り込める程度です。

要因の数が20個のケースについて、5ターン分で比較すれば、3分割する方法で2.6個、一つずつ確認する方法で5個ですから、3分割する方法にそれほどメリットは感じないかもしれません。(ちなみに、要因数17で逆転しますので、要因数が17個までは、ひとつずつ怪しいチェックボックスを減らしていく方法をおすすめします。)

ここで、勘が鋭い人は、3つ目の『グループA+グループC』のテストは消去法でしなくても良いのではないか?と思われたかもしれません。

2機能の組合せバグ(ダブルバグ)と分かっていればその通りですが、3機能の組合せバグ(トリプルバグ)の場合は『グループA+グループC』のテストでも再現しないことがあります。その場合は、グループを4つに分けてやり直しです。

そうなると、トリプルバグよりもさらに多くの組合せ(4機能の組合せや5機能の組合せ・・・)でバグが発生するケースが気になりますが、無則の組合せでそのような高次のバグが出ることは極めて稀なことなので、そこは安心してください。

おしまい。