不具合撲滅運動（続）

# イントロ

前回書いた記事「**[不具合撲滅運動](https://qiita.com/ORESAMAChanMg/items/3899b974ada763b74521)**」では、**不具合が出たときの分析ポイントのまとめ**を試みましたが、結局のところ不具合は出るよねって話がありました。

今回も（いろいろ思案した結果）やっぱりそのテーマで書きたくて「**続（編）**」となります。

決してネタが無いわけではなく、いろいろ考えたり、周りから煽られて実装にトライしようともしましたが、マネジメントばかりやってきた人間のコーディング力の無さをアピるのもちょっと（楽しそうだけど）自虐的でどうかなと思った次第です。
いや、違うな。**不具合を撲滅したい**んです😁

# では本題

今回のお題目は、**テスト技法**に焦点を当てたいと思います（ソフトウェア開発における・・・です）。
まぁ色々あるので、難しいことは専門の方に任せるとして、ちょっと触りをざっと理解できるような構成で行こうと思います。

テスト仕様書を書くときに、「**あ、ここのテストってあのやり方（テスト技法）がいいんじゃね？**」というような**気付きが生まれることを期待**しています。

## テスト技法って何？（べた）

コーディングばかりやっているとテスト技法なんて言葉にぶち当たることもない可能性ありますよね。
（そもそも、テストが嫌いな人が多い気がするので、技法に踏み込む人は更に稀な気もする）
なので、一応書いておきます。

**テスト技法**というからには**相応のノウハウの事**を指します（この後、色々紹介します）。
これを知っていると何が良いかというと、作り出したモノの**特性に応じたテスト仕様書（テストケース）を作り出す**ことができて、**テストの抜け漏れなどが防げます**。
またそういったテスト手順を踏むと非常に**効率的で無駄のないテストを実施することができる**ようになります。
そういうテストができるということは、**テスト品質がアップすることで不具合の早期発見に繋がり、不具合を撲滅できる**のです😁

## テスト技法の紹介の前に

テスト技法には様々なものがありますので大まかな分類について、少し触れておこうと思います。
選定する時の参考になればと思います。

よくある2軸での分類（ポジショニングマップ）によく使われるものとして以下のようなものがあります。

- **網羅的**かどうか　⇔　**ピンポイント**なものか
    - 言葉通り、コードに対するテストアプローチ
- **ホワイトボックス的**か　⇔　**ブラックボックス的**か
    - 仕様や設計、コード、想定できるエラーに対するテストであればホワイトよりな感じ
    - 顧客要求（ユーザー指向、満足度）や要件といった概念に近そうなものにあたるテストであればブラックよりな感じ
- **プログラム特性別**
    - テスト対象とする以下のような箇所にフォーカスをあてる
    - 論理判断、モジュール単位、バッチ（順次）処理、状態遷移、並列処理

※技法を集めてマッピングしてみるのも面白いかもですが、脱線しそうなので興味のあるかたは調べてみてください。

## テスト技法
全部は紹介しきれない（し、たぶん知らない）ので、比較的利用しやすいと個人的に考えているものを中心にピックアップします。

### ホワイトボックステスト

恐らく一番有名だと思います（で、一番煙たがられるテストな気もします）。
**コードの論理構造が正しいか**どうか、それこそ**1ステップずつ見ていく**ようなテストになります。
最近だとデバッガでステップ実行していく行為がそれに当たったりします。ちょっと複雑なアルゴリズムの確認などに利用するイメージです。
ただ裏を返すと確認する焦点がとても狭いため、要件を満たすかどうかといった視野・視点のテストではありません。また並列処理をしているようなコードのテストにも向きません。
この技法だけの利用や、意図を履き違えたテストを行うとザルなテストになりかねないので、それを突き詰めたカバレッジ（全命令コード・特定条件ステップに対してどの程度のテストを実行したかどうか）を出すことで、品質指標のひとつとする場合もあります。

※カバレッジについては後述します。

### ブラックボックステスト

よくプログラムってどんなもの？とか、処理の例えとして、「ある値を渡す（インプット）と答えを返す（アウトプット）」という、あのブラックボックスです。
**インプットに対して、想定しているアウトプットを返すかどうかを確認するテスト**になります。
ホワイトボックスと違って、**コードの内容は見ずにインプット・アウトプットのみに焦点**を当てています。ブラックボックスのテスト対象はプログラム構造やテストフェーズによって適切なものを選択します。
ホワイトボックスよりブラックボックスの方がちょっと楽なイメージを持っているかもですが（無意識に、または安易に用いる技法は、こっちな気がします）、このテストはインプット→アウトプットの組み合わせが複雑になればなるほど網羅的に実施することが難しくなるきらいがあるため、インプットのパターンを明確にするためのテスト技法がありますので、次はそれを紹介します。

#### 同値分割

一気に馴染みがなくなってしまったかもですが、同値分割というのは、無数にあるインプットとして渡す値を同値クラスという概念で括ってしまいます。その括る基準として**有効値／無効値などアウトプットが一定になるインプット値の範囲を同値クラス**とします。
その**同値クラスの中で代表的な値（少なくとも１つ）を用いて、全同値クラスのテストを行う**ことが、同値分割と呼ばれています。

#### 境界値分析

同値分割で作られたある**同値クラスの値群が、順序付けできる**値群であった場合、その**最小値と最大値をピックアップしてテスト**を行う方法です。個人的には値群（母集団）が大きなものだったりした場合、最小値＋２～３、最大値－２～３とか、中央付近の値とか少し広範囲に渡ってピックアップしテストデータにします。

#### デシジョンテーブル

同値分割、境界値分析とだんだんインプットへの考え方が複雑になってきていますが、さらに考えられるパターンとしてインプットが複数項目に渡りまた各項目が複数の値を取りうる、かつ項目間に相関関係があるような場合、**１項目ｘ値のパターンに対する各項目間の組み合わせとその結果を**下図のような**表にまとめてテスト**を行います。


| input | case1 | case2 | case3 |
|:-:|:-:|:-:|:-:|
| 項目１ － 有り | ★ | ★  | -  |
| 項目１ － 無し | - | -  | ★  |
| 項目２ － １～３ | ★ | -  | ★  |
| 項目２ － ４～８ | - | ★  | -  |
|  **output** | A  | B  | C   |

（デシジョンテーブルを作成するまでの整理方法・図解としてCFD法というものがありますが、名前だけ出して割愛🙇）

#### 状態遷移

**状態が遷移していく様を確認するテスト**です。遷移の基本は開始、終了があるものがシンプルですが、多数の遷移イベントを持ち次々と状態が変化していくような比較的大きなものもあります。状態遷移表と呼ばれるマトリクス表にまとめてテストを行います（図示する方法もある）。
表の構成は縦軸に各種状態、横軸に（そこで発生する遷移）イベントをならべ（縦横逆でも可）、クロスしたところにどういう状態か（そこでどうあるべきか）と、イベントが発生したときに遷移する状態を書きます。
そこそこ**複雑な仕様だと、設計時に書いてしまう**こともあります。
更に言うとこういうものからプログラムを吐き出すような仕組みもあったりします。

| ↓ 状態 | イベント → | 遷移直後 | Aボタンクリック | Bボタンクリック | 数値入力 |
|:-:|:-:|:-:|:-:|:-:|:-:|
| 1. | メニュー表示状態 | メニュー表示 | 2.に遷移 | 3.に遷移 | 指定画面に遷移 |
| 2. | X画面表示状態 | X画面モーダル表示 | （割愛） | モーダル閉じ、1.に遷移 | （割愛） |
| 3. | Y画面表示状態 | Y画面モーダル表示 | （割愛） | （割愛） | （割愛） |


#### 全数テスト

ここにきて、初心に帰る感じです。
はい、全数テストです。言葉の通り**インプット側で取りうる可能性のある値は全て確認するテスト**となります。状況に応じて採用しないとえらい目に会います。ホワイトボックステストに似ていますが、こちらは**全てがテスト範囲**だと思っています。
本当に全てテストしているか（網羅できているか）については、やはりカバレッジを出すことで確認します（後述）


#### 直交表

ちょっとこれ、統計学？入ってきます。ほぼ名前の紹介だけでサラッと流します🙇
敢えて全数テストの後に書いた理由ですが、これを用いることができる場合は、テストケースをグッと少なくすることができるからです。ちょっと理論はややこしいですが、全数テストなんてやってられないってときに調べてみるのもありかと思います。


## テスト技法（その他）

### ユースケース

1個のサブジェクト（コンポーネント、システム・・・誤解を生みそう）に対する、1個以上のアクター（人、役割、外部システム）との相互作用による振る舞いを表すユースケースシナリオをテストするもので、ここで見つける不具合は要件レベルだったり、UX的な観点の欠陥だったりします。

### モンキーテスト

仕様や操作方法を知らない人🙈がシステムの前に立ち、狂ったサルのように（ウソ）システムをいじって、何か怪しい点が無いか（UX的だったり、応答速度だったり、導線や業務フローがイメージしやすいかだったり）を確認するテストです。ある程度の経験則に基づいたランダムテストのようなものとなります。
節度を持ってあたらないと不具合管理表が質問だらけになったりします。
<img width="200px" alt="kids_chuunibyou_girl.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2149410/a36f9be2-d036-e113-312b-b1825509777f.png">
初版リリース前のシステムテストフェーズとかに、プロジェクトに関わっていなかったメンバーを1名～複数名、ソフトウェアの開発規模に応じてテスターとして異世界召喚して実施させたりします。


## その他（品質保証におけるメトリクス指標）

### カバレッジ
ここまでで何度か登場した単語です（これで締めよう）。
網羅、ホワイト、全数などといった**隅々まで確認する系のテスト**が、実際**どの程度実施できているかを示す**指標です。
指標とする基準は３つ（３段階？）あって、それぞれ

- **C0**（しーぜろ）:
    - 命令網羅率、命令部分にのみ着目した網羅率の算出方法です
- **C1**（しーわん）:
    - 分岐網羅率、if文を最低1回通すことに着目した網羅率の算出方法です
- **C2**（しーつー）:
    - 条件網羅率、if文の条件を全て通すことに着目した網羅率の算出方法です

という、**基準の条件にマッチする**ポイント（着目した）**ステップをどれだけ通過したかをパーセントで表現**します。
計測する単位はモジュール別だったりファイル別だったりしますが、網羅できているかを測る指標なので、ソフトウェアを構成するコード全てに対して計測するのが品質保証の面でも良いと思います。
なお**C2が一番厳しいテスト基準**となります。
個人的にはC1ぐらいで流して、あとは他の技法で品質担保を図りたいなというところです。
いろいろ調べると、**C1とC2の間ぐらいのMC/DC（改良条件判定）というものもある**ようです。

※**NASA(NESC)はPMにMC/DCカバレッジ100%を求める**らしい。。。厳しい

### 循環的複雑度（サイクロマティック複雑度）
カバレッジで締めようと思ったんですが、ちょっと脱線で。
テストする**コードに不具合が含まれやすい傾向を把握することができる**というもので、それは１つのモジュールに妙に入り組んだコードが書かれていたり、コード量が多すぎるという点を確認することで判定することができます。
それを**静的解析の段階で数値化してくれる**というなんともありがたい（有難迷惑な場合もありますが）考え方（循環的複雑度、またの名をサイクロマティック複雑度、Cyclomatic Complexity）があるので、紹介しておきます。
その判定方法ですが、if文やfor、switchなど繰り返しや分岐するポイントをカウントして、モジュール単位で複雑度を数値化します。
またその数値が**一定基準を超えるものに対しては**、テストを実施する前に**リファクタリングなどのアプローチを検討する**といったことが一般的です。

なお基準となる複雑度の閾値は結構まちまちだったりするのですが、少し紹介しておきます。

|複雑度|コメント|
|:-:|:-|
|～10|シンプルなコード、テストは容易|
|11～20|若干複雑なコード、テストは慎重に|
|21～50*|はっきり言って複雑なコード、テスト設計が困難で不具合を発見できない可能性が大きい|
|50*～|不具合アリ〼、保守とかしたくありません|

※**50**を**40**に変更したり、複雑度を出し、一定数値以内に収めるようルール付けする（コーディング規約に載せる）こともある
※**NASA(NESC)はPMに15以下を求める**っぽい。超えるとリファクタリングってことかと。厳しい。

なお、算出してくれる静的解析ツールはいろいろある（循環的複雑度の算出作業そのものには手を出さない方がいいですｗ）ので、それを利用して複雑度を見ておき、テスト前にコードのリファクタリングや、責務の再検討、アーキテクチャの見直しなどをやっておくと、テスト自体が軽くなり不具合を埋め込みにくくなります。

# まとめ
というほど何をまとめとすれば良いかわかりませんが（最後の最後で脱線も若干してしまったし）、ソフトウェアのテストを如何に漏れなく、効率的にこなすかという点において、技法の理解があるとテストケースを出すときに勘所もよくなると思いますので、ちょっと大変なテスト作業に入る前に眺めて貰えたら、不具合を撲滅できる"きっかけ"になるやもしれません。
あとテストを書くという文化が浸透しつつあります（テスト駆動開発なんかもそう）が、こういうものを理解して行われているかどうかが重要だと思いますので、テストケースを考える際の一助になれば幸いです。

**テスト品質がアップすることで不具合の早期発見に繋がり、不具合を撲滅できる**と信じています。

ここまで、駄文にお付き合いいただき、ありがとうございました🙇

# おまけ

[NASA(NESC)の文献](https://ntrs.nasa.gov/citations/20205011566)
→脱線した理由。

あと、今回は続編なので、前回分を貼っておきます。

https://qiita.com/ORESAMAChanMg/items/3899b974ada763b74521