単体テストの品質上げる！深いドメインの技術課題に立ち向かう方法

# 読者
* プロダクトの品質をあげたいエンジニアの方
* 単体テストを自分の感覚で書いてしまいがちなエンジニアの方
* テストケースが大量にあって、減らしたいと思っているエンジニアの方
* 深いドメインが必要なプロダクトで戦っている方

フロントエンジニア、バックエンドエンジニア、QAエンジニア、SRE、SATどんな役割の方でも品質をあげるヒントになればと思います。



# ペアワイズとは
```
ソフトウェアのバグの多くが1つまたは2つの因子の組み合わせによって発生している という事実に基づいてテストケースを作成する方法です。
```
2つの組み合わせのテストだけしておけば、だいたいOKという方法です。

フルメッシュの要素で開発やテストを行うとデータ量に心が折れる場合もあると思います。
ペアワイズを使ってテストケースを減らし、テスト品質を落とさないようにするにはペアワイズ法がオススメです。

単体テスト時に、メッソッドの引数を因子にしてペアワイズ法で単体テストを書くと効率性と品質のバランスがちょうどいい品質になりますので、ぜひ活用しましょう。


# モデルデータ

障害者福祉：[放課後等デイサービスの算定構造](https://www.mhlw.go.jp/content/12200000/001205331.pdf#page=58)をサンプルに進めます。


![001205331.pdf 2024-02-25 14-52-48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51607/51f7b21f-09e4-eb93-eaa4-4db2ac952675.png)



## 因子を抽出します。

因子を抽出します。今回は、すでに構造化されていますが、何もない場合の方が多く
因子を抽出するために構造表をつくるとはいいかと思います。

抽出した因子は以下の表になります。

| 因子 | 水準 | 
| -------- | -------- | 
| 障害児  | 医療的ケア児（３２点以上）     |
| ^    | 医療的ケア児（１６点以上）     |
| ^    | 医療的ケア児（３点以上）     |
| ^    | それ以外     |
| 定員   | 定員10人以下 |
| ^    | 定員11人以上20人以下 |
| ^    | 定員21人以上 |
| 時間区分 |区分1  |
| ^ |区分2  |
| ^ |区分3  |
| 休日 |学校休  |
| ^ |放課後  |

　この休日は算定構造表だけではわからず、算定要件を読まないとわからなかったりします。
プロダクトの品質をつくるためには、ドメイン知識が必要という前提があります。
ドメイン駆動設計でドメインオブジェクトを作っている場合は、値オブジェクトにしてもいいと思います。
コードにちゃんとドメイン知識が反映されていると認知負荷が下がりコード品質も向上するのでオススメです。


# PICTをつかってみる

ペアワイズ法を効率的に実施できるツールがあります。マイクロソフトが開発したソフトウェアテストツール。
Pairwise Independent Combinatorial Testingの略(以下、PICT)で、テスト ケースとテスト構成を生成します。PICT を使用すると、手動で生成したテストよりも効果的なテストを、実践的なテスト ケース設計に必要な時間のほんのわずかな時間で生成できます。
[公式のドキュメントはこちらです](https://github.com/Microsoft/pict/blob/main/doc/pict.md)

## インストール

インストールは簡単。
```
brew install pict
```
以上です！


標準出力をテーブルにしてくれる以下のツール([naoty/table](https://github.com/naoty/table))も入れておくと便利です。
```
brew tap naoty/misc
brew install table
```

## テストデータをつくる

インストールが終了したら、テキストエディタでテストデータを作ります。
フォーマットは```因子:水準の形式```でデータを並べて記述します。

```テストデータ(hday-pict.txt)
障害児:医療的ケア児（３２点以上）,医療的ケア児（１６点以上）,医療的ケア児（３点以上）,それ以外
定員:定員10人以下,定員11人以上20人以下,定員21人以上
時間区分:区分1,区分2,区分3
休日:学校休,放課後

# 制約条件
# 学校休業日は区分3は請求できない
IF [時間区分] = "区分3" THEN [休日] IN {"学校休"};
```

PICTですが、制約条件をテストデータに反映させることができます。([書き方](https://github.com/Microsoft/pict/blob/main/doc/pict.md))
この場合だと時間区分3は、学校が休日の場合のみ算定可能というドメインルールを反映しています。

データと、制約条件が1つのファイルに記述できるのは単一責任の原則にあるように1つのクラスには1つの責務があるべきという状態を意識しながらクラス設計するのにすごく役に立ちます。

```単一責任の原則
1つのクラスは1つだけの責任を持たなければならない。
すなわち、ソフトウェアの仕様の一部分を変更したときには、それにより影響を受ける仕様は、そのクラスの仕様でなければならない。
```

## 実行してみましょう

実行はpictコマンドのオプションにファイル名をしてください。
制約条件ありでフルメッシュでパターンをつくると60パターンですが、ペアワイズにすると14パターンになります。
区分3は学校休日の場合しか組み合わせされてないので制約条件も反映されています。

```/o:N    - Order of combinations (default: 2)```

複雑な業務ルールがある場合はorder オプションを3にして組み合わせパターンを増やしたり調整しましょう。
このオプションを1にすると、それぞれの水準が1つ使われパターン抽出されます。
正常系1つだけ確認というような品質基準を目指すなら、それもいいでしょう。
品質基準に合わせてトレードオフして使ってみてください。


```
% pict hday-pict.txt /o:2 | table -H
+----------------------------+----------------------+----------+--------+
| 障害児                      | 定員                 | 時間区分 | 休日   |
+----------------------------+----------------------+----------+--------+
| それ以外                    | 定員10人以下          | 区分1    | 学校休 |
| それ以外                    | 定員21人以上          | 区分2    | 放課後 |
| 医療的ケア児（３点以上）       | 定員11人以上20人以下   | 区分3    | 学校休 |
| 医療的ケア児（３点以上）       | 定員21人以上          | 区分1    | 放課後 |
| 医療的ケア児（３点以上）       | 定員10人以下          | 区分2    | 放課後 |
| それ以外                    | 定員21人以上          | 区分3    | 学校休 |
| それ以外                    | 定員11人以上20人以下   | 区分1    | 放課後 |
| 医療的ケア児（１６点以上）     | 定員11人以上20人以下   | 区分2    | 学校休 |
| 医療的ケア児（１６点以上）     | 定員10人以下          | 区分1    | 放課後 |
| 医療的ケア児（３２点以上）     | 定員11人以上20人以下   | 区分1    | 学校休 |
| 医療的ケア児（３２点以上）     | 定員21人以上          | 区分2    | 放課後 |
| 医療的ケア児（３２点以上）     | 定員10人以下          | 区分3    | 学校休 |
| 医療的ケア児（１６点以上）     | 定員21人以上          | 区分2    | 放課後 |
| 医療的ケア児（１６点以上）     | 定員10人以下          | 区分3    | 学校休 |
+----------------------------+----------------------+----------+--------+
%
```

# パターンができたそのあとに
ペアワイズ法で生成された14パターンができました。
このあと、このデータを元に、単体テストでモックデータをつくるのもいいです。
テストエンジニアなら、これからテストケースを作っていくのもいいかと思います。
また、今回作成したhday-pict.txtファイルをgitで管理してPR時にレビューをして単体テストの網羅性を話し合うのもいいかもしれません。

皆様の品質を向上させるための、ヒントになると嬉しいです。

## TIPS
個人的には設計時に作成し、これをクラス設計・ドメイン設計の補助ツールとして作成することを提案したいです。
テスト駆動開発という言葉もありますが、実装したあとにペアワイズで表を作って設計漏れを発見しても手戻りが大きくなります。設計時の品質検証に使うのはいいのでないかと考えてます。

こういうテスト技法は、実装後の動的テストで使われる鉄板な手法ではありますが、ドメイン知識の言語化をするときに、ドメインモデル図だけでなくPICTのテストデータを作りながら設計するのもありかもですね。テストデータと業務ルールが1つのファイルにあるという状態は1つのコンテキストでもあるわけなので、コンテキストを見極めるときに整理しながらつくるのがお勧めです。

直交表とか作りながら要求分析が必要な深いドメイン知識を求められるプロダクトには相性のいいやり方だと思います。