BLOG ブログ
テスト設計におけるさまざまな手法
yuji takeichi
ソフトウェアのテストでは主に、テスト計画、テスト設計、テスト実装、テスト実施、テスト結果報告の流れで進みます。
本記事ではその中のテスト設計について手法いくつか紹介したいと思います。
ブラックボックステスト
手法 | 概要 |
---|---|
同値分割 | 入力データを同じ結果が期待されるグループに分け、各グループから代表値を選んでテストする手法 |
境界値分析 | 境界付近でのエラー発生を予測し、境界値を中心にテストする手法 |
デシジョンテーブルテスト | 複数の条件とその結果を表形式でまとめ、全ての条件組み合わせを網羅する手法 |
状態遷移テスト | システムの状態とその遷移をモデル化し、遷移の組み合わせをテストする手法 |
ペアワイズテスト | 各入力のペアを重点的にテストする手法 |
原因結果グラフ法 | 原因と結果の条件を視覚的に整理し、条件間の関係をグラフ化してテストケースを導き出す手法 |
直交表テスト | 直交表を用いて、入力条件の組み合わせを最小限に抑えながら、全体のカバレッジを確保する手法 |
オールペア法 | すべての入力ペアの組み合わせをテストし、重要なペア間の相互作用をカバーする手法 |
探索的テスト | 事前にテストケースを定めず、自由にシステムを操作しながらテストする手法 |
エラー推測 | テスターの経験や知識を活かして、バグが潜んでいそうな箇所を予測してテストする手法 |
ホワイトボックステスト
手法 | 概要 |
---|---|
制御フローテスト | プログラムの制御構造(条件分岐、ループなど)に注目してテストケースを設計する手法 |
パスカバレッジテスト | すべての実行可能なパスを網羅するようにテストケースを設計する手法 |
ステートメントカバレッジテスト | プログラムの各ステートメントが少なくとも一度実行されることを確認する手法 |
条件カバレッジテスト | 条件式内の各部分条件がそれぞれ真 と偽 になるようにテストを設計する手法 |
各手法の例
以下のシナリオを元にいくつかの例を出したいと思います。
シナリオ: システムのパスワードバリデーションをテストする。パスワードの要件は以下の通りです。
長さ:8文字以上
文字種:英大文字、英小文字、数字、記号の少なくとも3種類を含む
同値分割
有効クラス
長さが8文字以上
大文字、小文字、数字、記号の少なくとも3種類を含む
無効クラス
長さが7文字以下
文字種が2種類以下
各クラスから代表的な値を選んでテストします。
テストケース1(有効): Abc12345
(長さが8文字以上、文字種3種類以上)
テストケース2(無効): abc123
(長さが6文字、文字種2種類)
境界値分析
長さの境界値
境界の直前(無効): 7文字
境界のちょうど(有効): 8文字
境界を超えた値(有効): 9文字
長さの境界に関するテストケース
Abc1234
(7文字:境界の直前)
Abc12345
(8文字:境界のちょうど)
Abc123456
(9文字:境界を超えた)
文字種の境界値
境界の直前(無効): 2種類の文字種
境界のちょうど(有効): 3種類の文字種
境界を超えた値(有効): 4種類の文字種
文字種の境界に関するテストケース
abcdef12
(小文字と数字の2種類のみ)
Abc12345
(大文字、小文字、数字の3種類)
Abc123!@
(大文字、小文字、数字、記号の4種類)
デシジョンテーブルテスト
すべての条件とその組み合わせを網羅的に検証します。
長さ | 大文字 | 小文字 | 数字 | 記号 | 結果 |
---|---|---|---|---|---|
8文字以上 | あり | あり | あり | あり | 有効 |
なし | 有効 | ||||
なし | あり | 有効 | |||
なし | 無効(数字と記号不足) | ||||
なし | あり | あり | 有効 | ||
なし | 無効(小文字と記号不足) | ||||
なし | あり | 無効(小文字と数字不足) | |||
なし | 無効(小文字と数字と記号不足) | ||||
なし | あり | あり | あり | 有効 | |
なし | 無効(大文字と記号不足) | ||||
なし | あり | 無効(大文字と数字不足) | |||
なし | 無効(大文字と数字と記号不足) | ||||
なし | あり | あり | 無効(大文字と小文字不足) | ||
なし | 無効(大文字と小文字と記号不足) | ||||
なし | あり | 無効(大文字と小文字と数字不足) | |||
なし | 無効(大文字と小文字と数字と記号不足) | ||||
8文字未満 | あり | あり | あり | あり | 無効(長さ不足) |
なし | 無効(長さ不足) | ||||
なし | あり | 無効(長さ不足) | |||
なし | 無効(長さ不足)(数字と記号不足) | ||||
なし | あり | あり | 無効(長さ不足) | ||
なし | 無効(長さ不足)(小文字と記号不足) | ||||
なし | あり | 無効(長さ不足)(小文字と数字不足) | |||
なし | 無効(長さ不足)(小文字と数字と記号不足) | ||||
なし | あり | あり | あり | 無効(長さ不足) | |
なし | 無効(長さ不足)(大文字と記号不足) | ||||
なし | あり | 無効(長さ不足)(大文字と数字不足) | |||
なし | 無効(長さ不足)(大文字と数字と記号不足) | ||||
なし | あり | あり | 無効(長さ不足)(大文字と小文字不足) | ||
なし | 無効(長さ不足)(大文字と小文字と記号不足) | ||||
なし | あり | 無効(長さ不足)(大文字と小文字と数字不足) | |||
なし | 無効(長さ不足)(大文字と小文字と数字と記号不足) |
状態遷移テスト
状態の遷移の組み合わせをテスト
初期状態 → パスワード入力 → バリデーション成功 → 完了
初期状態 → パスワード入力 → バリデーション失敗 → エラーメッセージ表示 → 再入力
ペアワイズテスト
各パラメータのペアがカバーされるように検証します。
(全ての組み合わせを網羅するわけではなく、2つのパラメータの相互作用を重点的に確認します。)
パラメータ:
長さ | 大文字 | 小文字 | 数字 | 記号 |
---|---|---|---|---|
8文字以上 | あり | あり | あり | なし |
8文字以上 | なし | あり | あり | あり |
8文字未満 | あり | なし | あり | あり |
8文字以上 | あり | なし | なし | あり |
8文字未満 | なし | あり | なし | なし |
8文字以上 | あり | あり | なし | なし |
この表では、すべての2つのパラメータの組み合わせ(ペア)が少なくとも1回はテストされています。
制御フローテスト
if文やforループが正しく動作しているかを確認し、すべての分岐が網羅されるようにテストケースを設計します。
const validatePassword = (password: string): string => {
const lengthValid = password.length >= 8;
const hasUpperCase = /[A-Z]/.test(password);
const hasLowerCase = /[a-z]/.test(password);
const hasDigit = /[0-9]/.test(password);
// 制御フローの分岐に着目
if (!lengthValid) {
return "Password is too short.";
} else if (!hasUpperCase) {
return "Password must contain at least one uppercase letter.";
} else if (!hasLowerCase) {
return "Password must contain at least one lowercase letter.";
} else if (!hasDigit) {
return "Password must contain at least one digit.";
} else {
return "Password is valid.";
}
};
// テストケース
console.assert(validatePassword("12345678") === "Password must contain at least one uppercase letter.", "Test Case 1 Failed");
console.assert(validatePassword("ABCDEFGH") === "Password must contain at least one lowercase letter.", "Test Case 2 Failed");
console.assert(validatePassword("Abcdefgh") === "Password must contain at least one digit.", "Test Case 3 Failed");
console.assert(validatePassword("Abc12345") === "Password is valid.", "Test Case 4 Failed");
上記のケースでは各if-else
の分岐に注目し、それぞれの条件を満たさない場合のパスをチェックしています。
パスカバレッジテスト
3つの異なる条件分岐がある場合、それぞれの条件が満たされるケース、満たされないケース、部分的に満たされるケースをテストして、すべての経路がカバーされることを確認します
const validatePassword = (password: string): boolean => {
const isValid = true;
if (password.length < 8) {
isValid = false;
}
if (!/[A-Z]/.test(password)) {
isValid = false;
}
if (!/[0-9]/.test(password)) {
isValid = false;
}
if (!/[a-z]/.test(password)) {
isValid = false; // パス4
}
return isValid;
};
// 各パスをカバーするテストケース
console.assert(validatePassword("Abcdef12") === true, "Test Case 1 Failed");<
console.assert(validatePassword("Abc123") === false, "Test Case 2 Failed");
console.assert(validatePassword("abcdefgH") === false, "Test Case 3 Failed");
上記の例ではすべての実行経路を網羅するため、各条件がどのように組み合わされても対応できるようにテストしています。
ステートメントカバレッジテスト
例えば、if文があるとすれば、その条件が真になるケースと偽になるケースを両方テストし、それによってすべてのコード行が実行されることを確認します。
条件カバレッジテスト
各部分条件がそれぞれ真と偽になるようなテストケースを設計します。
条件式「A && B」の場合、次のようなテストケースを作成します。
- Aが真でBが真→条件式が真
- Aが真でBが偽→条件式が偽
- Aが偽でBが真→条件式が偽
- Aが偽でBが偽→条件式が偽