1. はじめに
これまで、プログラミング言語やフレームワークについては、書籍などで基礎を学び、実務を通して知識を身につけてきました。一方、ソフトウェアテストについて体系的に学ぶ機会はなく、過去のテストケースや自身の経験をもとにテストケースを作成していました。
実装者がテストケースの作成からテスト実施までを担当する場合、自分自身がどのようなテストケースを設計するかが、ソフトウェアの品質に大きく関わります。
しかし、実装時には十分にテストしたつもりでも、考慮できていなかった条件による不具合や、仕様変更・改修に伴う既存機能への影響など、テストケースの不足を感じる場面がありました。
そこで、これまで経験則で行っていたテストについて基礎から学びなおし、テスト観点や代表的なテスト設計技法について整理しました。
本記事では、学習したテスト設計技法を整理するとともに、これまでの自身のテストケース作成方法と照らし合わせながら、実務でどのように活用できるかを考えます。
参考資料
布施昌弘、江添智之、永井努、三堀雅也 著
『この1冊でよくわかる ソフトウェアテストの教科書 品質を決定づけるテスト工程の基本と実践』SBクリエイティブ、2021年.
2. これまでのテストケース作成
これまでは、仕様や実装内容から必要なテストケースを考え、類似機能がある場合には過去のテストケースを参考にしていました。
その中で、入力値の境界や条件の組合せなども意識していましたが、それらをテスト設計技法として体系的に理解していたわけではありませんでした。
今回の学習を通して、これまで経験則で行っていたことにも、
- 入力値をグループに分けて考える
- 境界となる値を確認する
- 複数の条件を組み合わせて確認する
など、テスト設計技法に通じる考え方が含まれていたことに気づきました。
一方で、新たに意識するようになったのが、「何をテストするのか」というテスト観点を整理し、対象に応じてテスト設計技法を選択するという考え方です。
次章から、このテスト観点とテスト設計技法について整理します。
3. テスト観点の整理
今回の学習で特に印象に残ったのが、テストケースを作成する前に「テスト観点」を整理するという考え方です。
テスト設計は、次のような流れで進めます。
- テストの目的を確認する
- 機能一覧を作成する
- テスト観点を抽出する
- テスト観点を機能へ割り当てる
- テスト技法を検討・適用する
これまでも、過去のテストケースを参考にしたり、仕様から必要な確認事項を考えたりすることで、結果としてテスト観点を考慮していました。
しかし、「どの機能に対して、どのような観点でテストするのか」を整理してからテストケースを作成するという意識はありませんでした。
テストマップ
機能とテスト観点の対応を整理する方法として、「テストマップ」があります。
テストマップでは、縦軸に機能一覧、横軸にテスト観点を配置し、それぞれの機能に対して必要なテスト観点を割り当てます。
例えば、次のようなイメージです。
| 機能 | 入力値 | 表示データ | 処理結果 | エラー | 権限 | 排他制御 |
|---|---|---|---|---|---|---|
| 検索 | ○ | ○ | ○ | ○ | ||
| 一覧表示 | ○ | ○ | ||||
| 登録 | ○ | ○ | ○ | ○ | ||
| 編集 | ○ | ○ | ○ | ○ | ○ | ○ |
| 削除 | ○ | ○ | ○ | ○ | ○ |
※ 一般的なCRUD機能を想定したテストマップの一例です。実際に必要となるテスト観点は、対象となる機能や仕様によって異なります。
このように整理することで、いきなり具体的なテストケースを考えるのではなく、機能ごとに必要なテスト観点を俯瞰してから、具体的なテストケースへ落とし込むことができます。
これまで過去のテストケースを参考にすることで暗黙的に再利用していたテスト観点も、テストマップとして整理することで、「何を確認する必要があるのか」を意識してテストケースを設計できると感じました。
- テストケースを作る前に「何を確認するのか」というテスト観点を整理する
- 「テストマップ」で機能とテスト観点の対応を可視化する
- 必要な観点を整理してから、具体的なテストケースへ落とし込む
では、抽出したテスト観点から具体的なテストケースを設計する際には、どのような方法があるのでしょうか。
4. テスト設計技法
今回の学習では、ブラックボックステストの代表的なテスト設計技法として、以下の4つについて学びました。
- 同値分割・境界値分析
- デシジョンテーブル
- 状態遷移テスト
- 組合せテスト
4.1 同値分割・境界値分析
同値分割は、入力値などを同じ結果になると考えられるグループに分割し、それぞれのグループから代表値を選んでテストする方法です。
例えば「1~100まで入力可能」という仕様であれば、入力値を次のようなグループに分けて考えることができます。
- 1未満
- 1~100
- 101以上
すべての値をテストするのではなく、それぞれのグループから代表となる値を選ぶことで、テストケース数を抑えながら確認できます。
一方、境界値分析では、同値クラスの境界となる値に着目します。
先ほどの例であれば、0、1、100、101などが確認対象になります。
実務でも入力項目の最小値・最大値などの境界を確認することはありましたが、今回の学習では、仕様書に明示された境界だけではなく、実装によって意図せず生まれる 「隠れた境界」 についても意識する必要があると学びました。
テスト時に境界値を確認するだけではなく、実装時に不要な境界を作らないことや、必要な境界がある場合には仕様や設計として明確にすることも重要だと考えます。
- 「同値分割」で入力値をグループ化し、「境界値分析」でその境目を確認する
- すべての値を確認するのではなく、代表値と境界値から効率的にテストケースを設計する
- 仕様上の境界だけでなく、実装によって生まれる「隠れた境界」も意識する
4.2 デシジョンテーブル
デシジョンテーブルは、複数の条件と、その組合せによって決まる動作を表形式で整理する方法です。
条件を表の上部、条件に対する動作を下部に配置し、それぞれの条件の組合せをルールとして列方向に整理します。
例えば、ある機能について、
- ステータス
- ユーザーの権限
- 特定の処理が完了しているか
といった複数の条件によって、ボタンの表示・非表示や編集可否が変わる場合に利用できます。
すべての条件を単純に組み合わせると、条件が増えるほどテストケースも急激に増加します。
そこで、成立しない条件の組合せを除外したり、ある条件によって結果が決まり他の条件が結果に影響しない場合には条件をまとめたりすることで、必要な組合せを整理できます。
| 条件・動作 | ルール1 | ルール2 | ルール3 | ルール4 |
|---|---|---|---|---|
| ステータスが「作成中」 | Y | Y | Y | N |
| 編集権限がある | Y | Y | N | - |
| 入力が完了している | Y | N | - | - |
| 申請ボタンを表示する | ○ | × | × | × |
デシジョンテーブルはテストケースを作成するときだけではなく、複数条件によって振る舞いが変化する仕様そのものを整理する際にも活用できると感じました。
- 複雑な複数条件による振る舞いを「デシジョンテーブル」で整理する
- 不要な組合せを省き、必要に応じて表を分割し、見やすさも意識する
4.3 状態遷移テスト
状態遷移テストは、システムの現在の状態と、発生するイベントによって、次にどの状態へ遷移するのかに着目する方法です。
例えば、「無料会員」「有料会員」「休会中」「退会済」などの会員状態と、「有料プランに申し込む」「休会する」「再開する」「解約する」といったイベントを持つ機能を考えます。
状態遷移図では、「どの状態から、どのイベントによって、どの状態へ遷移するのか」を視覚的に整理できます。
一方、状態遷移表では、状態とイベントの組合せを表形式で整理することで、正常な遷移だけでなく、起こりえない組合せについても確認できます。
| 現在の状態 | 有料申込 | 休会 | 再開 | 解約 |
|---|---|---|---|---|
| 無料会員 | 有料会員 | N/A | N/A | 退会済 |
| 有料会員 | N/A | 休会中 | N/A | 退会済 |
| 休会中 | ? | N/A | 有料会員 | 退会済 |
| 退会済 | N/A | N/A | N/A | N/A |
表に整理すると、「休会中に有料申込を行った場合はどうなるのか」という未定義の組合せがあることに気づきます。
今回の学習で特に重要だと感じたのは、文章で仕様が定義されていても、状態遷移図や状態遷移表として整理することに意味があるという点です。
文章だけでは気づきにくい状態やイベントの組合せも、実際に図や表へ落とし込む過程で可視化され、仕様の抜け漏れを確認しやすくなります。
単純な状態変化であれば文章だけでも十分ですが、状態やイベントが複雑な場合には、状態遷移図や状態遷移表を活用したいと考えました。
- 状態遷移図で「状態」と「イベント」を可視化する
- 状態遷移表で起こりえない状態・イベントも可視化する
- 文章で仕様が書かれていても、状態遷移図と状態遷移表を書く過程で仕様の抜け漏れを確認できる
4.4 組合せテスト
組合せテストは、複数の因子を組み合わせてテストすることで、特定の組合せで発生する不具合を確認する方法です。
例えば、
操作方法 × 作成パターン × 設定の有無
のように複数の因子が関係する機能では、それぞれを個別に確認するだけでは、特定の組合せで発生する不具合を見落とす可能性があります。
実際に過去の不具合を振り返ってみても、単一の条件ではなく、複数の条件が重なった場合にのみ発生するものがありました。
一方、すべての因子の組合せを網羅しようとすると、因子や値が増えるほどテストケース数も大きくなります。
そこで今回の学習では、すべての組合せを網羅するのではなく、2因子間の組合せを網羅する考え方について学びました。
All-Pairs法などを利用することで、テストケース数を抑えながら、2因子間の組合せを効率的に確認できます。
組合せテストでは、単純に網羅性を高めるのではなく、テストにかかるコストとのバランスを考えることが重要だと感じました。
- 「組合せテスト」で、複数の因子が組み合わさった場合の不具合を確認する
- 費用対効果を考え、2因子間の組合せを効率的にテストする
5. テスト設計技法の選択
ここまで複数のテスト設計技法について整理しましたが、重要なのは、すべての機能にすべての技法を適用することではありません。
今回の学習を通して、テスト対象の特徴に応じて、適切なテスト設計技法を選択するという考え方への理解が深まりました。
| テスト対象の特徴 | 適用できる技法 |
|---|---|
| 入力値をいくつかのグループに分けられる | 同値分割 |
| 最小値・最大値などの境界がある | 境界値分析 |
| 複数の条件によって結果が変化する | デシジョンテーブル |
| 状態とイベントによって振る舞いが変化する | 状態遷移テスト |
| 多数の因子の組合せを確認する | 組合せテスト |
これまでは、仕様や実装内容から必要と思われるテストケースを直接考えることが多く、どのようなテスト設計技法を適用するかを意識することはあまりありませんでした。
しかし、適切な技法を選択するためには、まず 「何をテストしたいのか」を明確にすることが重要 です。
第3章で整理したテスト観点やテストマップを利用して「何をテストする必要があるのか」を整理し、その特徴に応じてテスト設計技法を選択することで、具体的なテストケースへ落とし込むことができます。
- 「何をテストしたいのか」を明確にする
- テスト対象の特徴に応じて、適切なテスト設計技法を選択する
- テスト観点 → テスト設計技法 → テストケースの順に具体化する
6. テスト自動化
これまで、テスト自動化というと、PHPUnitやJestなどを利用してテストコードを書くことをイメージしていました。実際にこれらを利用してテストコードを作成した経験はありましたが、テスト自動化全体の流れについて意識したことはありませんでした。
しかし、テスト自動化には次のような工程があります。
- 自動化する範囲を決める
- テストデータを作成する
- テストケースを作成する
- テストスクリプトを作成する
- テストを実行する
- メンテナンスする
PHPUnitやJestによるテストコードの作成は、この中の 「テストスクリプトを作成する」という一工程 です。
また、すべてのテストを自動化するのではなく、複数の条件について同じテストを繰り返し実施する場合など、自動化の効果が期待できる範囲を選択することも重要だと学びました。
今後は、まずテスト観点を整理し、適切なテスト設計技法を選択した上で、必要な箇所からPHPUnitやJestなどを利用したテストスクリプトの作成に取り組みたいと考えています。
- テストスクリプトの作成は、テスト自動化の一工程
- 自動化の効果が期待できる範囲を選択する
- テスト設計を行った上で、必要な箇所から自動化する
7. まとめ
今回、ソフトウェアテストについて体系的に学ぶことで、これまで経験則で行っていたテストケースの作成を、テスト観点やテスト設計技法という形で整理することができました。
特に、テストケースをいきなり作成するのではなく、
テスト観点を整理する → 適切なテスト設計技法を選択する → テストケースへ落とし込む
という流れを意識することが重要だと学びました。
今後は、今回学んだ考え方を自身のテスト設計に取り入れながら、必要な箇所ではテスト自動化にも取り組み、より効果的なテストを設計できるようにしていきたいと考えています。