境界値テスト・等価クラステスト

本記事は[Software Testing A Craftsman’s Approach](https://malenezi.github.io/malenezi/SE401/Books/Software-Testing-A-Craftsman-s-Approach-Fourth-Edition-Paul-C-Jorgensen.pdf)のChapter 5, 6を参考に作成しています．

:::note warn
本記事はソフトウェア工学初心者が書いているため，記述や解釈に誤りが含まれている可能性があります．
:::

## 境界値テスト(Boundary Value Testing)

境界値テストでは，関数を定義域(Domain)から値域(Range)への写像として見ます．境界値テストは２つの独立した観点から４つに分類することができます．１つ目の観点は無効値(invalid value)を考慮するかです．無効値を考慮する場合は **Robust testing** となります．２つ目の観点は単一欠陥仮定(single fault assumption)を仮定するかです．これを仮定してない，複数欠陥仮定を用いる場合は **Worst-Case testing** となります．

:::note info
**単一欠陥仮定(single fault assumption)について**
[Software Testing A Craftsman’s Approach](https://malenezi.github.io/malenezi/SE401/Books/Software-Testing-A-Craftsman-s-Approach-Fourth-Edition-Paul-C-Jorgensen.pdf)において単一欠陥仮定は*This assumes that faults are due to incorrect values of a single variable.* とされていました．
$a$or$b$という引数と$1$or$2$という引数の２つの引数を受け取る関数$f(\cdot ,\cdot)$について考えます．この関数が，
$$
f(a,1) = \text{ok }, f(a,2) = \text{ok }, f(b,1) = \text{failure }, f(b,2) = \text{failure}
$$
となれば，２つめの引数に関わらず１つめの引数に$b$を入れたことがfailureの原因なので，単一欠陥仮定が成り立ちます．一方，
$$
f(a,1) = \text{ok }, f(a,2) = \text{ok }, f(b,1) = \text{ok }, f(b,2) = \text{failure}
$$
となる場合，$f$は$b,2$という組み合わせに対してのみfailureを引き起こすので，単一欠陥仮定が成り立ちません．
:::

これらの観点で分類された境界値テストを，以下の表にまとめます．

|                  | 無効値なし | 無効値あり | 
| ---------------- | ---------- | ---------- | 
| 単一欠陥仮定 |    Normal boundary value testing        |     Robust boundary value testing       | 
| 複数欠陥仮定 |      Worst-case boundary value testing      |     Robust worst-case boundary value testing       | 

それぞれの種類のテストのイメージを持つために，以下のような値域を持つ関数$F(\mathrm{x}_1,\mathrm{x}_2)$を考えます．

$$
\mathrm{a} \leq \mathrm{x}_{1} \leq \mathrm{b}
$$

$$
\mathrm{c} \leq \mathrm{x}_{2} \leq \mathrm{d}
$$

この関数に対して，それぞれのテストでは，黒点で示されたテストケースを選択します．

![](https://cdn.mathpix.com/cropped/2024_06_10_84d54a1083115f624c2cg-112.jpg?height=518&width=691&top_left_y=266&top_left_x=524)
*Normal boundary value testing*

![](https://cdn.mathpix.com/cropped/2024_06_10_84d54a1083115f624c2cg-114.jpg?height=520&width=691&top_left_y=267&top_left_x=524)
*Robust boundary value testing*

![](https://cdn.mathpix.com/cropped/2024_06_10_84d54a1083115f624c2cg-115.jpg?height=518&width=691&top_left_y=266&top_left_x=524)
*Worst-case boundary value testing*

![](https://cdn.mathpix.com/cropped/2024_06_10_84d54a1083115f624c2cg-115.jpg?height=520&width=691&top_left_y=970&top_left_x=524)
*Robust worst-case boundary value testing*

また，先に上げた区分以外にも， **特別値テスト(Special Value Testing)** というテストも重要になってきます．これは，関数の値域の境界だけではなく，テスターの経験やドメイン知識から関数の"soft spot"となるところを境界として扱うテストです．例えば`NextDate()`という，与えられた年月日がら次の日を求める関数について考えます．この場合，値域は月始，月末，年始，年末ですが，暦に関するドメイン知識があれば閏年の2/29付近がsoft spotとなり，ここに重点的に負荷をかけることができます．

### 具体例

#### Test Cases for the Triangle Problem

`classifyTriangle()`という関数について考えます．この関数は、3つの整数 \( a, b, c \) を入力として受け取り、三角形の種類を分類しています。以下は入力と出力の関係を説明します：

- **入力**: 整数 \( a, b, c \) （各辺の長さを表す）
- **出力**: `TriangleType` 列挙型の値（三角形の種類を示す）

具体的には、出力は以下のいずれかになります：
- `TriangleType::Equilateral`: \( a, b, c \) の三辺がすべて等しい正三角形
- `TriangleType::Isosceles`: \( a, b, c \) のうち少なくとも2辺が等しい二等辺三角形
- `TriangleType::Scalene`: \( a, b, c \) のすべての辺が異なる不等辺三角形
- `TriangleType::NotATriangle`: \( a, b, c \) のいずれかの条件（三角形の不等式）を満たさない場合、三角形ではないことを示す

このとき，辺の長さを1以上200以下とすると，Normal boudary value testingtとWorst-Case Boundary Value testingのテストケースは以下のようになります．

*Normal boundary value testing*

| Case | $\mathrm{a}$ | $\mathrm{b}$ | $\mathrm{c}$ | Expected Output |
| :--- | :---: | :---: | :---: | :---: |
| 1 | 100 | 100 | 1 | Isosceles |
| 2 | 100 | 100 | 2 | Isosceles |
| 3 | 100 | 100 | 100 | Equilateral |
| 4 | 100 | 100 | 199 | Isosceles |
| 5 | 100 | 100 | 200 | Not a triangle |
| 6 | 100 | 1 | 100 | Isosceles |
| 7 | 100 | 2 | 100 | Isosceles |
| 8 | 100 | 100 | 100 | Equilateral |
| 9 | 100 | 199 | 100 | Isosceles |
| 10 | 100 | 200 | 100 | Not a triangle |
| 11 | 1 | 100 | 100 | Isosceles |
| 12 | 2 | 100 | 100 | Isosceles |
| 13 | 100 | 100 | 100 | Equilateral |
| 14 | 199 | 100 | 100 | Isosceles |
| 15 | 200 | 100 | 100 | Not a triangle |

*Worst-Case Boundary Value testing*

| Case | $\mathrm{a}$ | $\mathrm{b}$ | $\mathrm{C}$ | Expected Output |
| :---: | :---: | :---: | :---: | :---: |
| 1 | 1 | 1 | 1 | Equilateral |
| 2 | 1 | 1 | 2 | Not a triangle |
| 3 | 1 | 1 | 100 | Not a triangle |
| 4 | 1 | 1 | 199 | Not a triangle |
| 5 | 1 | 1 | 200 | Not a triangle |
| 6 | 1 | 2 | 1 | Not a triangle |
| 7 | 1 | 2 | 2 | Isosceles |
| 8 | 1 | 2 | 100 | Not a triangle |
| 9 | 1 | 2 | 199 | Not a triangle |
| 10 | 1 | 2 | 200 | Not a triangle |
| 11 | 1 | 100 | 1 | Not a triangle |
| 12 | 1 | 100 | 2 | Not a triangle |
| 13 | 1 | 100 | 100 | Isosceles |
| 14 | 1 | 100 | 199 | Not a triangle |
| 15 | 1 | 100 | 200 | Not a triangle |
| 16 | 1 | 199 | 1 | Not a triangle |
| 17 | 1 | 199 | 2 | Not a triangle |
| 18 | 1 | 199 | 100 | Not a triangle |
| 19 | 1 | 199 | 199 | Isosceles |
| 20 | 1 | 199 | 200 | Not a triangle |
| 21 | 1 | 200 | 1 | Not a triangle |
| 22 | 1 | 200 | 2 | Not a triangle |
| 23 | 1 | 200 | 100 | Not a triangle |
| 24 | 1 | 200 | 199 | Not a triangle |
| 25 | 1 | 200 | 200 | Isosceles |

## 等価クラステスト(Equivalence Class Testing)

等価クラステストには，2つの動機があります．完全なテストを実現したいというものと，冗長性を避けたいというものです．等価クラステストは境界値テストの2つの観点，**Robustness(頑健性)** と**単一／複数欠陥仮定** を反映しています．よって，境界値テストと同様に，等価クラステストは以下の4種類に分類できます．

|                  | 無効値なし | 無効値あり | 
| ---------------- | ---------- | ---------- | 
| 単一欠陥仮定 |    Weak Normal Equivalence Class Testing        |   Weak Robust Equivalence Class Testing       | 
| 複数欠陥仮定 |      Strong Normal Equivalence Class Testing        |     Strong Robust Equivalence Class Testing       | 


それぞれの種類のテストのイメージを持つために，以下のような値域を持つ関数 $ F(\mathrm{x}_1, \mathrm{x}_2) $ を考えます．

$$
\mathrm{a} \leq \mathrm{x}_{1} \leq \mathrm{d}
$$

$$
\mathrm{e} \leq \mathrm{x}_{2} \leq \mathrm{g}
$$

ここで，$ \mathrm{x}_1 $ の値域は閉区間 $ [\mathrm{a}, \mathrm{d}] $ で，$ \mathrm{x}_2 $ の値域は閉区間 $ [\mathrm{e}, \mathrm{g}] $ です．

この関数 $ F $ のテストでは，有効な値の等価クラスとして $ V1 = \lbrace \mathrm{x}_1 : \mathrm{a} \leq \mathrm{x}_1 < \mathrm{b} \rbrace, V2 = \lbrace \mathrm{x}_1 : \mathrm{b} \leq \mathrm{x}_1 < \mathrm{c} \rbrace, V3 = \lbrace \mathrm{x}_1 : \mathrm{c} \leq \mathrm{x}_1 \leq \mathrm{d} \rbrace, V4 = \lbrace \mathrm{x}_2 : \mathrm{e} \leq \mathrm{x}_2 < \mathrm{f} \rbrace, V5 = \lbrace \mathrm{x}_2 : \mathrm{f} \leq \mathrm{x}_2 \leq \mathrm{g} \rbrace $  があります．

また，無効な値の等価クラスとしては，$ NV1 = \lbrace \mathrm{x}_1 : \mathrm{x}_1 < \mathrm{a} \rbrace, NV2 = \lbrace \mathrm{x}_1 : \mathrm{x}_1 > \mathrm{d} \rbrace, NV3 = \lbrace \mathrm{x}_2 : \mathrm{x}_2 < \mathrm{e} \rbrace, NV4 = \lbrace \mathrm{x}_2 : \mathrm{x}_2 > \mathrm{g} \rbrace $  が定義されています．

これらの等価クラスは互いに素であり，全体として平面全体を覆っています．

この関数に対して，それぞれのテストでは，黒点で示されたテストケースを選択します．

![](https://cdn.mathpix.com/cropped/2024_06_18_4761e47c07211b592cd5g-133.jpg?height=535&width=982&top_left_y=1676&top_left_x=383)
*Weak Normal Equivalence Class Testing*

![](https://cdn.mathpix.com/cropped/2024_06_18_4761e47c07211b592cd5g-134.jpg?height=523&width=964&top_left_y=261&top_left_x=387)
*Strong Normal Equivalence Class Testing*

![](https://cdn.mathpix.com/cropped/2024_06_18_4761e47c07211b592cd5g-134.jpg?height=520&width=957&top_left_y=1678&top_left_x=391)
*Weak Robust Equivalence Class Testing*

:::note warn
上記のテストケースだと，$\mathrm{x}_1, \mathrm{x}_2$のいずれも無効値である場合しかテスト出来ていないので，$\mathrm{x}_1, \mathrm{x}_2$のどちらがfailureの原因かが特定できません．これを特定できるようにしたものが以下になります．

![](https://cdn.mathpix.com/cropped/2024_06_18_4761e47c07211b592cd5g-135.jpg?height=515&width=962&top_left_y=265&top_left_x=388)
*Revised Weak Robust Equivalence Class Testing*
:::

![](https://cdn.mathpix.com/cropped/2024_06_18_4761e47c07211b592cd5g-135.jpg?height=513&width=962&top_left_y=1638&top_left_x=388)
*Strong Robust Equivalence Class Testing*

### 具体例

#### Test Cases for the Triangle Problem

[先程の三角形を分類する関数](###Test-Cases-for-the-Triangle-Problem-1)に対するWeak Normal Equivalence Class Testingは以下になります．

*Weak Normal Equivalence Class Testing*

| Test Case | a | b | c | Expected Output |
| :--- | :---: | :---: | :---: | :---: |
| WN1 | 5 | 5 | 5 | Equilateral |
| WN2 | 2 | 2 | 3 | Isosceles |
| WN3 | 3 | 4 | 5 | Scalene |
| WN4 | 4 | 1 | 2 | Not a triangle |

ここで，平面で図示した例のように，有効値に対して$a,b,c$をsubintervalに分割してクラス分けをすることが出来ないため，Strong Normal Equivalence Class TestingはWeak Robust Equivalence Class Testingと等価になります．

これに対して，無効値を考慮した(Revised) Weak Robust Equivalence Class Testingは以下のようになります．

*Weak Robust Equivalence Class Testing*

| Test Case | $\mathrm{a}$ | $\mathrm{b}$ | $\mathrm{c}$ |  |
| :--- | :---: | :---: | :---: | :--- |
| WR1 | -1 | 5 | 5 | Value of $\mathrm{a}$ is not in the range of permitted values |
| WR2 | 5 | -1 | 5 | Value of $\mathrm{b}$ is not in the range of permitted values |
| WR3 | 5 | 5 | -1 | Value of $\mathrm{c}$ is not in the range of permitted values |
| WR4 | 201 | 5 | 5 | Value of $\mathrm{a}$ is not in the range of permitted values |
| WR5 | 5 | 201 | 5 | Value of $\mathrm{b}$ is not in the range of permitted values |
| WR6 | 5 | 5 | 201 | Value of $\mathrm{c}$ is not in the range of permitted values |

更に，有効値クラスと無効値クラスの相互作用を考慮する場合は以下のようなテストケースを考慮する必要があります．以下のテーブルは0未満の無効値に関する無効値を考慮した場合に付け足されるテストケースを示しています．

*Strong Robust Equivalence Class Testing*

| Test Case | $\mathrm{a}$ | $\mathrm{b}$ | $\mathrm{c}$ | Expected Output |
| :--- | :---: | :---: | :---: | :--- |
| SR1 | -1 | 5 | 5 | Value of $\mathrm{a}$ is not in the range of permitted values |
| SR2 | 5 | -1 | 5 | Value of $\mathrm{b}$ is not in the range of permitted values |
| SR3 | 5 | 5 | -1 | Value of $\mathrm{c}$ is not in the range of permitted values |
| SR4 | -1 | -1 | 5 | Values of $\mathrm{a}, \mathrm{b}$ are not in the range of permitted values |
| SR5 | 5 | -1 | -1 | Values of $\mathrm{b}, \mathrm{c}$ are not in the range of permitted values |
| SR6 | -1 | 5 | -1 | Values of $\mathrm{a}, \mathrm{c}$ are not in the range of permitted values |
| SR7 | -1 | -1 | -1 | Values of $\mathrm{a}, \mathrm{b}, \mathrm{c}$ are not in the range of permitted <br> values |