ブラックボックステストは特定の入力に対して正しい出力が返されるかを判定するユーザ視点のテスト。
仕様を満たしているかどうかがテスト観点になる。
内部構造を考慮しないため、たまたま正しい出力が返されたのか否かは判定できず、潜在的なバグを見つけられない。
同値分割
同等の結果が返される入力値の集合を同値クラスと呼ぶ。
つまり、ある値で欠陥が検出されるとき、同じ同値クラスの他の値でも同じ欠陥が検出される。
逆も同様で、ある値で欠陥が検出されないとき、同じ同値クラスの他の値でも検出されない。
同値クラスを洗い出し、各同値クラスから1つの値のみをテストすることでテストケース数を減らせる。
たとえば特定の範囲が有効な入力のとき、その範囲が有効な同値クラス、その範囲よりも上と下がそれぞれ無効な同値クラスであり、合計で3つの同値クラスがある。
境界値分析
同値クラスの範囲の境界でテストすることを境界値分析と呼ぶ。
欠陥は境界値で発生することが多いため、効率的に検出できる。
組み合わせテスト
条件の組み合わせに対する結果をテストすることを組み合わせテストと呼ぶ。
組み合わせテストには有則のものと無則のものがある。
- 有則
- 条件の組み合わせごとに結果が仕様として決まっているもの
- 仕様通りに実装されているかを評価するためにテストする
- 仕様として決まっている範囲なので組み合わせが膨大な数になりにくい
- デシジョンテーブルを使う
- 無則
- 有則でないもの
- 特定の組み合わせで意図しない不具合が発生しないかを検出するためにテストする
- 仕様として決まっていないので組み合わせが膨大な数になりやすい
- すべての組み合わせをテストすることはできないので、2つや3つの条件の組み合わせだけを網羅することが多い
- 直交表やペアワイズ法などを使う
- ペアワイズ法による有名なテストケース作成ツールとして、Microsoftが作成した PICT がある
デシジョンテーブル
条件の組み合わせに対する結果をまとめた表をデシジョンテーブルと呼ぶ。
- ある処理におけるすべての条件を縦に並べる
- その下にすべての結果を縦に並べる
- 条件と結果は区別できるようにする
- 条件の組み合わせの数だけルールを横に並べる
- 条件の組み合わせを埋める
- 列ごとに結果を埋める
例
1 | 2 | 3 | 4 | |
---|---|---|---|---|
年齢8歳以上 | Y | Y | N | N |
身長130cm以上 | Y | N | Y | N |
ジェットコースター乗車可能 | x | |||
ジェットコースター乗車不可能 | x | x | x |
特定の条件のみで結果が決まる場合、それ以外の条件を省略することでルール数を削減できる。
省略された条件は -
などを記入するとわかりやすい。
1 | 2 | 3 | |
---|---|---|---|
年齢8歳以上 | Y | N | - |
身長130cm以上 | Y | - | N |
ジェットコースター乗車可能 | x | ||
ジェットコースター乗車不可能 | x | x |
矛盾している条件がある場合、結果には N/A (Not Applicable) と記載する。
1 | 2 | 3 | 4 | |
---|---|---|---|---|
年齢18歳以下 | Y | Y | N | N |
年齢65歳以上 | Y | N | Y | N |
割引あり | N/A | x | x | |
割引なし | N/A | x |
条件が3値以上ある場合、各欄に直接値を記入することでルール数を削減できる。
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
ゴールド会員 | Y | Y | Y | Y | Y | Y | Y | Y | N | N | N | N | N | N | N | N |
シルバー会員 | Y | Y | Y | Y | N | N | N | N | Y | Y | Y | Y | N | N | N | N |
購入金額10000円以上 | Y | Y | N | N | Y | Y | N | N | Y | Y | N | N | Y | Y | N | N |
購入金額5000円未満 | Y | N | Y | N | Y | N | Y | N | Y | N | Y | N | Y | N | Y | N |
10%割引 | N/A | N/A | N/A | N/A | N/A | x | N/A | N/A | ||||||||
5%割引 | N/A | N/A | N/A | N/A | N/A | x | N/A | x | N/A | |||||||
3%割引 | N/A | N/A | N/A | N/A | N/A | x | N/A | x | N/A | |||||||
割引なし | N/A | N/A | N/A | N/A | N/A | N/A | x | N/A | x | x | x |
↓
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | |
---|---|---|---|---|---|---|---|---|---|
会員種別 | ゴールド | ゴールド | ゴールド | シルバー | シルバー | シルバー | 非会員 | 非会員 | 非会員 |
購入金額 (p) | p >= 10000 | 5000 <= p < 10000 | p < 5000 | p >= 10000 | 5000 <= p < 10000 | p < 5000 | p >= 10000 | 5000 <= p < 10000 | p < 5000 |
10%割引 | x | ||||||||
5%割引 | x | x | |||||||
3%割引 | x | x | |||||||
割引なし | x | x | x | x |
↓
1 | 2 | 3 | 4 | 5 | 6 | 7 | |
---|---|---|---|---|---|---|---|
会員種別 | ゴールド | ゴールド | ゴールド | シルバー | シルバー | シルバー | 非会員 |
購入金額 (p) | p >= 10000 | 5000 <= p < 10000 | p < 5000 | p >= 10000 | 5000 <= p < 10000 | p < 5000 | - |
10%割引 | x | ||||||
5%割引 | x | x | |||||
3%割引 | x | x | |||||
割引なし | x | x |
パラメタライズドテスト
パラメータ化テストとも呼ばれる。テスト設計ではなくテスト実装の技法。
テストコードとテストデータを分離し、テストデータをパラメータとして渡すことで、複数の入力値と期待値の組によるテストを共通化する。
また、仕様変更などで結果が変わったり、テストケースを増減させたりしたときに修正しやすくなる。
プロパティベーステスト
テスト対象が取りうる任意の入力に対して、出力が満たす性質(プロパティ)を検証するテスト。
一般的にテストと言った際に指す、特定の入力に対して特定の出力になることを検証するテストは、事例ベーステスト、実例ベーステスト、Exampleベーステストなどと呼ばれる。
たとえば整数xとyの和を計算する関数 add(x, y)
のテストを行うとする。
事例ベーステストでは以下のように入出力の組ごとにテストする
- xとyが1と2のとき、出力が3
- xとyが3と5のとき、出力が8
- etc
プロパティベーステストでは入力に任意の整数をランダムかつ大量に渡して、以下のようにテストする。
add(x, y)
がadd(y, x)
と等しい- 可換則
add(x, add(y, z))
がadd(add(x, y), z)
と等しい- 結合則
また、配列を昇順にソートする関数のプロパティベーステストは以下のようになる。
- 配列の長さが変わらないこと
- 配列に含まれる要素が変わらないこと
- 配列の先頭が配列内の最小値になっていること
- 配列の末尾が配列内の最大値になっていること
よく使われるプロパティの例は以下の通り。
- 逆の操作をすると元に戻ること
- エンコード/デコード
x == decode(encode(x))
- シリアライズ/デシリアライズ
x == deserialize(serialize(x))
- 配列のreverse
arr == arr.reversed().reversed()
- エンコード/デコード
- 複数の操作を違う順序で実行しても同じ出力になること
- 配列をmapしてからソート、ソートしてからmap
arr.map(x => x + 1).sorted() == arr.sorted().map(x => x + 1)
- 配列をmapしてからソート、ソートしてからmap
- 操作をしても変わらないこと
- 配列をmapしたときの配列の長さ
arr.length == arr.map(x => x + 1).length
- 配列をソートしたときの各要素
arr.sorted().forEach(x => arr.includes(x))
- 配列を複数回ソートしたときの各出力
arr.sorted() == arr.sorted().sorted()
- 配列をmapしたときの配列の長さ
事例ベーステストでは同値クラスを網羅する入出力の組を洗い出さないとバグを検知できないことがあるが、プロパティベーステストはそれを防げる。
ただ、大量の入力でテストするのでテスト時間が長くなりやすい。
また、具体的な値がテスト内に記述されないため、直感的に理解しにくい。
事例ベーステストとプロパティベーステストを組み合わせるのが理想。