ソフトウェアの基本的なテスト技法

今更ながらテストに関する初歩的な本を読んだので、記事投稿の練習も兼ねて自分なりの要約としてまとめる。(他にもほぼ同じ内容でおそらく当記事よりもわかりやすい記事が散見されたが気にせずまとめる:rolling_eyes:、情報が古かったり間違ってる可能性もありますm(__)m)
本は以下↓

https://www.amazon.co.jp/dp/4822282511

※ ~~ホワイトボックステストについても後日追記する予定です。~~
ようやく書けました。(遅い) → [ホワイトボックステスト](#ホワイトボックステスト)

# ブラックボックステスト
テスト対象ソフトウェアの内部構造、実装などは考えず、要件と仕様書だけをよりどころにしたテスト。
UT(単体テスト)からUAT(ユーザー受入テスト)までどの段階でも使える。

## 同値クラステスト
  - 意識しなくてもよく使っている場合が多い基本のテスト。特に連続するテストデータがあるシステムの場合に、大幅にテストケースを削減できる。
  - テストケースで同じ結果を返すことが期待される入力値の範囲を**同値クラス**という。
  - 無効値に対するテストを作成するべきかについてはそのシステムの設計仕様による。
  - 無効値テストがいらないものは**契約による設計**という事前条件(入力値)が満たされた場合のみ事後条件(特定の処理)が行われるという考えに基づいて作られたシステム。
  - ただ**契約による設計**システムはwebなど一般的なユーザーが使用するシステムとしてはよろしくないので(無効値の入力の際に動作停止する)、普通は**防御的設計**という設計アプローチが用いられる。こちらは無効値に対するエラーも通知するため無効値テストが必要。
  - 基本的に無効値テストは複数の入力条件がある場合はそれぞれ一つずつ無効となる値を試すテストケースが必要になる。(以下の表のように)
 
| 入力条件1 | 入力条件2 | 入力条件3 |
| ---- | ---- | ---- |
| **無効値** | 有効値 | 有効値 |
| 有効値 | **無効値** | 有効値 |
| 有効値 | 有効値 | **無効値** |


## 境界値テスト
  - こちらもよく使われる。同値クラステストの派生。
  - ミスが発生しやすい連続するテストデータの上端、下端に対する処理にピックアップしたもの。
  - 仕様で定められている境界値(a)の **すぐ上(x>a)** と **すぐ下(x<a)** のテストケースを選択する。
  - この **すぐ上** と **すぐ下** の幅は扱うデータの単位によって異なる。(整数値なら-1,+1、小数値なら-0.1,+0.1など)
  - 入力項目が複数ある場合は境界値における有効値と無効値の組み合わせでテストケースを作成する。


## デシジョンテーブルテスト
  - そのシステムでのビジネスルールを可視化するテスト。以下の表を元にテストケースを作成する。

|  | ルール1 | ルール2 | ルール3 |
| ---- | ---- | ---- | ---- |
| 条件1 | true | true | false |
| 条件2 | true | false | DC |
| … |  |  |  |
| アクション1 | true | false | false |
| アクション2 | true | true | false |
| … |  |  |  |

  - 条件は **入力値** を、アクションは **出力値** を、ルールは **入力値の組み合わせ** = **テストケース** を示している。
  - 条件はtrue,falseのような2値だけでなく、具体的数値や範囲を示す<xなども入れられる。その場合は同値クラスや境界値テストのように具体的な値を抽出してその分テストケースを増やす必要がある。
  - 出力値(アクション)に影響がない入力値(条件)は **DC(Don't Care=任意)** として表せる。


## ペア構成テスト
  - 入力項目が多くテストすべき組み合わせが膨大な場合に効果的なテスト。
  - 入力項目(x個)に対してそれぞれの入力値(y個)があった場合、すべての組み合わせはyのx乗個となる。当然そんな数は現実的ではないので、そういった場合はペア構成テストが使える。
  - 端的に言うと **入力項目をそれぞれのペアで見た時に、取りうる入力値の組み合わせが全て現れるテストケースだけでテストを行う** という手法。よくわからないので下の表を例にとる。(入力項目：4個(列)、入力値：3種類(表内値))
 
|  | 1 | 2 | 3 | 4 |
| ---- | ---- | ---- | ---- | ---- |
| **1** | 1 | 1 | 1 | 1 |
| **2** | 1 | 2 | 2 | 2 |
| **3** | 1 | 3 | 3 | 3 |
| **4** | 2 | 1 | 2 | 3 |
| **5** | 2 | 2 | 3 | 1 |
| **6** | 2 | 3 | 1 | 2 |
| **7** | 3 | 1 | 3 | 2 |
| **8** | 3 | 2 | 1 | 3 |
| **9** | 3 | 3 | 2 | 1 |

  - この表の列の中で任意のペア(2列)に注目してみると、いずれのペアも1~3の値の組み合わせが全て現れていることが確認できる。そのため、実施するテストケースとしてはこの9個(行)のみで良いというのがペア構成テスト。
  - しかし肝心なこの表はどこから出てきたのかというと、この表は [**直行表**](https://w.wiki/324e)といいある一定の大きさごとに要素のすべての組み合わせが現れる特別な表のことである(詳しくは↑のwikiで…)。つまりこの特別な性質を持つ表である直行表の大きさは **自動的に決まってくる。**
  - 例えば先ほどの例だと、4個の項目に対して3この値をとる場合は9*4の直行表が最適な大きさとなる。

<details><summary>**ここからは直行表の詳しい話**</summary>
　これより多い入力項目のものに関してはさらに大きな直行表が当てはまることになるが、必ずしも最適な直行表の大きさにならないこともある(直行表の大きさは一定の大きさで決まっているため)。その場合はテストで必要になる組み合わせの数を上回る大きさの直行表を探して当てはめる必要がある。
　しかし、直行表の大きさが大きい分実際の入力項目数以上の列や種類数が現れることになるので、その個所は有効値に置き換えてテストを行う必要がある。=直行表の行数分は必ずテストする必要がある。
※直行表は本来は水準や因子といった直行表を算出するうえで基準となる値を設定して作成するが、実際のテストケースに見合う最適な直行表を求めるのはかなり難しいと思われます、より詳細はググってみてください。
参考：[直行表を求めるサイト](http://iq148.com/tools/orthogonal_table/)
</details>

  - 直行表以外のアプローチとして **全ペアアルゴリズム** というものがある(こちらのほうが扱いやすいと思う…)。こちらは最適なペアの組み合わせを算出するプログラムがすでにあるためそちらを用いる方法。
  - 本でも紹介されていたこのプログラムは http://www.satisfice.com/tools.shtml で公開されているそうです(allpairsの方)。[**有志の方が実際に使用されておられました。**](https://tosi-tech.net/2012/07/generate-testcase-by-allpairs-algorithm/)
  - また[**Pairwiser**](https://inductive.no/pairwiser/)やmicrosoftの[**Pict**](https://jaccz.github.io/pairwise/)という全ペア抽出ツールもあるそうです。


## 状態遷移テスト
  - ロジックではなくシステムの状態という観点でのテスト方法。(あるいは文書化の方法)
  - **状態遷移図** か **状態遷移表** を用いてテストor文書化を行う。
  - 状態遷移図は以下のようなもので、主に **状態を表す円** と **遷移を表す矢印** 、そして状態を遷移させる **イベント(矢印のラベル)** 、イベントに/を挟んで記述するイベントに伴って発生する操作である **アクション**などで構成される。

![状態遷移図](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/272646/d655ff35-9375-3566-3fe4-3b23c64a88ae.png)

  - また図にはないが、イベントラベルに記す遷移の条件判定を表す **真偽値[]** などもある。
  - 状態遷移表は以下のような表で表され、 **無効な状態遷移の組み合わせ** なども表として書き起こせる点が利点。

| 現在の状態 | イベント | アクション | 次の状態 |
| ---- | ---- | ---- | ---- |
| チケット申し込み | 入金 | - | 入金済み |
| チケット申し込み | 支払期限切れ | - | 未払いキャンセル |

  - 状態遷移図および表を使ったテストでは、**全遷移を必ず一回は通るテストケース**=**矢印を全て通るテストケース**を実施するのが望ましい。
  - 無効な遷移であってもリスクが高いものなどは追加してテストすることも必要。


## ドメイン分析テスト
  - 複数の変数に相互関係(影響しあう)があり、その境界値を検証するテスト。
  - 本での紹介例では一定の範囲の値をとる二つの変数(x<=36, y<-4.0など)の相互関係のテストで示されていた。
  - 上記のような連続的な値をとり、境界値が存在する変数においては **on(境界値)**、 **off(境界値のすぐ上orすぐ下)**、**in(任意の有効値)**、 **out(任意の無効値)** という四つのポイントからテストケースを割り出す。
  - **off** についてはonが有効値(<=,>=の場合)の場合は **無効値** 、onが無効値(<,>の場合)の場合は **有効値** として設定する。
 
例：

|  | x<=36 | x<36 |
| ---- | ---- | ---- |
| on | 36 | 36 |
| off | **37** | **35** |
| in | 25,30など | 25,30など |
| out | 45,50など | 45,50など |

  - 実際にテストケースを作成する際には **ドメインテストマトリックス** という表を作成する。
  -  例として二つの変数 *x<=36, y<-4* のドメインテストマトリックスを以下に示す。

![ドメインテストマトリックス](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/272646/add86b32-e3a1-ec38-cf3e-43fdae5624fb.png)

  - この表は、片方の変数の代表値であるinの値と他方の変数のon,off値の組み合わせで構成されている(最後は互いの代表値の組み合わせ)。これは変数の数や条件が増えた場合でも同じようにテストケースを増やす。


## ユースケーステスト
  - アクター(ユーザーなど)の目的に観点を置いたテスト。
  - アクターがシステムを使用して目的を達成するためのシナリオ(操作の流れ)を、テストケースとして定義する方法。すべてユーザー視点からテストを定義する。以下はユースケース図の例。

![ユースケース図](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/272646/97543fbd-9a6d-20bb-dba0-600909310cf8.png)

  - テストケース作成の際には、 **リスクが高いトランザクション(処理の塊)を外さないよう** に作成する。
  - 成功シナリオとそこから拡張したシナリオについて、それぞれ **最低一つずつテストケースを作成する** ようにする。
  - テストケースで実際に入力する値については規定していないので、同値クラステストや境界値テストを用いて入力する値を決定する。(正常系や異常系)
  - また反対に必ず失敗するようなシナリオや突飛な操作を含むシナリオなども同時に検証してみることもできる。
  - このテストはシナリオという観点ではある程度のカバレッジを満たすことはできるが、厳密な入力値の検証などは伴わないので他のテストと必ず併用するようにするのがよいと思う。

# ホワイトボックステスト
テスト対象のシステムの内部パス、構造や実装をよりどころとするテスト(ブラックの反対)
一定のプログラミングスキルが必要になる。UT(単体テスト)、IT(結合テスト)、ST(システムテスト)で主に使える。
システム内のパスをテストするものであり、モジュール間、サブシステム間、システム間とスケールを変えて実施できる。


## 制御フローテスト
  - プログラム内の実行パスを識別して、そのパスを網羅するように実施するテスト。しかし全網羅は膨大なパス数になることがほとんどなので適切な網羅率(=カバレッジ)でテストを実施することになる。
  - 制御フローテストは制御フロー図というものを描いてテストケースを考える。制御フロー図は **制御ブロック**・**判定ポイント(分岐)**・**合流ポイント**を矢印でつないで表現する。 

![制御フロー図の部品](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/272646/255f7394-6b4f-4993-9883-3543d6406f85.jpeg)

  - カバレッジのレベルとしては以下のような種類がある。
  <dl>
    <dt>レベル0(やってはならない)</dt>
    <dd>開発側でできるところまでテストして後はユーザーに投げる。つまりテストの破綻。(なぜこのレベルが書かれているかは分からないが現実にそのようなプロジェクトが転がっていることもあるのでそうならないようにという自戒の意味で書かれてるっぽい)</dd>
    <dt>レベル1</dt>
    <dd>すべての制御ブロックを一回は通るようにするというレベルのカバレッジ。<strong>ステートメントカバレッジ</strong>という。これだとパスとしては場合によっては1通りだけでも網羅してしまうレベルだが、1通りのパスだけでは当然カバレッジとして不十分。※厳密にはシステムには例外(ネットワーク切断やメモリリークなど)時の制御ブロックも存在しているが、そういったステートメントまで網羅するかはシステムの仕様やプロジェクトの方針で変わってくると思われる。</dd>
    <dt>レベル2</dt>
    <dd>すべての判定ポイントにおいて<strong>真と偽</strong>を一回は通るようにするカバレッジ(switchなら各caseを一回ずつ)。<strong>デシジョンカバレッジ(ブランチカバレッジ)</strong>という。個人的にはこのレベルのテストでも結構大変だと思う。</dd>
    <dt>レベル3</dt>
    <dd>レベル2では判定ポイントの真と偽のみを判定するが、その判定ポイントが複合条件だった場合(主にAND,ORなどで結合された条件)各条件の真偽まではテストしない。その各条件の真偽を一回は通るようにするレベルのカバレッジ。<strong>コンディションカバレッジ</strong>という。</dd>
    <dt>レベル4</dt>
    <dd>デシジョン・コンディションを組み合わせて複数条件の判定ポイントで各条件の全ての真偽の組み合わせをテストするレベルのカバレッジ。</dd>
    <dt>レベル5</dt>
    <dd>レベル4に加えて判定ポイントの複数条件判定の評価をコンパイラがどのように行うかまで考慮に入れ、すべてのパスを通るようにするカバレッジ。<strong>複合コンディションカバレッジ</strong>という。</dd>
    <dt>レベル7</dt>
    <dd>レベル5で通らない条件・パスも洗い出し一つのパスの漏らしもないようにするカバレッジ。全網羅。<strong>100%パスカバレッジ</strong>という。※ループ処理などがある場合は実質到達不可。</dd>
    <dt>(レベル6)</dt>
    <dd>ループ処理が存在する場合に、<strong>ループ0回・ループ一回・代表的なループ回数n回・最大ループ回数m回</strong>のテストをするカバレッジ。</dd>
  </dl>
  - また制御フローテストを行う上で**構造化テスト(基礎パステスト)**という手法もテスト実施に当たって基本となる。
  - 構造化テストは上記カバレッジの**レベル2**までを担保できる。
  - 構造化テストの手順は

  1. 制御フロー図の作成する。
  1. 作成した図から**サイクロマチック数**を求める。
  1. サイクロマチック個数分のパスの組み合わせを選択する。
  1. 選択したパスからテストケースを作成して実施する。

となる。

  - このサイクロマチック数とは**全てのリンク(矢印)を通る、独立でループを含まない最小のパスの個数**のこと。
  - 以下のように求められる。
 
> (サイクロマチック数)C = リンク数(矢印) - ノード数(制御ブロックの円) + 2

  - 3.の手順のパスの組み合わせの具体的な方法は以下

  1. 基準となる実行パスを選択する。※この基準となるパスは一番一般的またはシステム上一番重要なパスを選択する。
  1. 次のパスを選択する際に基準パスの一番目の判定ポイントの結果を変更する。(一番目の判定ポイント以外の判定ポイントは基準パスと同じ)
  1. さらに次のパスは基準パスの二番目の判定ポイントの結果を変更する。(二番目の判定ポイント以外の判定ポイントは基準パスと同じ)
  1. 2~3を繰り返し、サイクロマチック数分のパスを選択する。

## データフローテスト
  - データ値を持つ変数の状態の遷移(ライフサイクル)を対象にしたテスト技法。
  - 主にモジュール内の変数の**定義・使用・消滅**の状態に着目して、状態の遷移に矛盾やミスが含まれていないかを検証する。
  - テストの手順としては、まず制御フロー図のようにデータフローの図を書き起こしてざっと検査し(静的データフローテストという)、その後そこからテストケースを起こしてテストを実行する(動的データフローテストという)。
  - 例としてx,y,zという三つの変数が存在するモジュールのテストを行うとする。その場合の各変数のフローを図として起こしたものが以下とする。

![データフロー図](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/272646/b33c6e9d-0096-eb69-a099-dc3b2ef5f7c7.jpeg)


  - この3変数のそれぞれの遷移を追って確認してみると以下のような各変数の問題点が割り出せる。

> **x: 定義→定義**
> **y: →使用(定義なし)**
> **y: 定義→消滅**
> **z: →消滅(定義なし)**
> **z: 消滅→使用**
> **z: 消滅→消滅**

  - ※静的データフローテストは上記のような手順で行えるが、全ての問題点を見つけることはできない。(配列やリストなどの構造体データや複雑なパス内の発生し得ないデータの遷移、インフラ要因の問題などは検証しきれない。)
  - 実際にテストケースとして起こす動的フローテストは以下の条件を満たす遷移パスを対象とする。

> **全ての変数の定義と使用が対応づけられる遷移パス**

  - このパスを求める手法として作成したデータフロー図から、先述の**制御フローテスト - 構造化テストの手順3**と同様の手法が使える。(基準遷移パスから段階的に分岐パスを変更して網羅していく手法)


# 読んでみて
自分の業務(web)を改めて振り返ってみるとここで取り上げているテスト手法を完璧に満たすような基準でテストは到底行えていないなと感じました。もちろんシステムやプロジェクトの性質によって状況は異なりますが、ここで書かれていることはwebに限らずあらゆるソフトウェアシステム内で実践可能な超根底的な知識だと思うのでテスト業務の際には常に頭の片隅に置いておくことに損はないと思います。