ソフトウェア開発者が知っておきたいテストの常識

記事を書いている私はエンジニア1年目の初心者です。

普段はユニットテストやE2Eテストを実装していますが、特にテスト担当者というわけでもなく、テストに関する知識はあまりありません。しかし、ソフトウェア開発に携わる以上、よく耳にするテスト関連の用語やポイントをある程度知っておきたいと思い、調べた内容をまとめました。

間違いや不足などありましたら、ご指摘いただけますと非常にありがたいです。

## ソフトウェアテストとは

ソフトウェアテストとは、仕様にないふるまいやバグを見つける作業のことだそうです。また、ソフトウェアテストにより見つかったバグを修正する作業のことをデバッグといいます。

ソフトウェアテストが目標とする品質は、試験項目にすべて合格することであり、すべてのバグを取り除くことではありません。テストによってバグを発見することはできますが、バグが存在しないことを証明することはできないからだそうです。

テストの目的には次のようなものがあります。

* バグの防止
* 要件を満たすことの検証
* 完成品の動作妥当性の確認
* 意思決定のための情報提供

## テストの種類

調べてみると、テストには多くの種類がありましたが、ここでは次の4つについて書きます。

* 単体テスト
* 結合テスト
* システムテスト
* 受け入れテスト

テストを実施する順番も、基本的には上記の順になるようです。

### 単体テスト（ユニットテスト）

関数やメソッドといった単位でテストを行うことを単体テストというようです。ユニットテストも同一のものです。

コード実装時などに開発者自身によって実施される場合が多いようです。まずはブラックボックステストを実施し、それをクリアしたものに対してホワイトボックステストを実施する、という説明を読みました。期待した出力が返ってくることを最初に確認し、出力には現れない状態の変化などを次に確認する、という意味だと解釈しましたが、違うかもしれません。

PHPにおける単体テストのツールにはPHPUnitがあります。

### 結合テスト（インテグレーションテスト）

結合テストは、単体テストをクリアした関数などの機能を結合させて、うまく連携して動作するかを確認するためのテストです。インテグレーションテストやジョインテストと呼ばれることもあるようです。

結合テストは、2つ以上のコンポーネントの連携を確認するものですが、連携する予定のコンポーネントがまだ存在しない場合はスタブやドライバなどのテスト用プログラムを使用します。このとき、上位と下位のどちらからコンポーネントを結合させていくかによって、トップダウンテストまたはボトムアップテストと分類されるようです。

### システムテスト

システムテストとは、プログラム単独ではなく、他のプログラムやハードウェア、ネットワーク、データベースなどと組み合わせて実施するテストのようです。

E2Eテストや非機能要件を満たすことのテストは、このシステムテストに含まれると思います。開発環境と本番環境が異なる場合は、システムテストは本番環境を借りて行うこともあるようです。データベースなどと組み合わせて実施するテスト、という定義ですが、データベース操作を伴う関数のテストは、これまでユニットテストだと思っていましたが、これもシステムテストなのでしょうか。

### 受け入れテスト

受け入れテストとは、システムが本番稼働する直前に実施されるテストです。

ユーザニーズや要件を満たし、システムがトラブルなくスムーズに運用できるかをテストする、と説明されていました。リリース直前に実施されることの多いテストなので、十分な時間が確保できない場合も多いようです。

要件を満たすかどうかは客観的に評価できますが、ユーザニーズを満たすか、スムーズに運用できるか、といった観点は主観的なものです。そのため、自動テストというよりは人間による最終チェックのようなイメージがあります。

開発者以外の人間がテストすることをアルファテスト、一般ユーザがテストすることをベータテストというようです。一般的にベータテストはリリース前に実施されるようですが、オープンソースソフトウェアの場合は**ベータ版**として広く公開され、宣伝も兼ねて実施される場合もあるようです。

## テスト関連の用語

スタブやモック、カバレッジといったソフトウェアテスト関連の用語の意味をまとめています。

### スタブ

スタブとは、単体テストもしくは結合テストを実行する際に、未実装もしくは未検証の下位コンポーネントの代わりに用いられる簡易プログラムのことです。

上位コンポーネントからの呼び出しに対して、テストに必要な最低限の出力のみを行うプログラムであることが多いようです。上位のコンポーネントを先に実装し、下位コンポーネントをスタブで代替するテストの方式は、トップダウンテストと呼ばれます。

### ドライバ

ドライバとは、ソフトウェアテストの対象となるコンポーネントを呼び出して、期待する結果が返ってくることを確かめるプログラムのことです。

通常はテスト対象ごとに作られるようです。結合テストで下位コンポーネントから順にテストしていく方式をボトムアップテストといいますが、未実装あるいは未検証の上位コンポーネントの代替となるものがドライバです。

### モックオブジェクト

モックオブジェクトとは、スタブの一種であり、テスト対象の下位コンポーネントの代わりに用いられるオブジェクトのことで、単にモックとも呼ばれます。

スタブとモックの違いですが、スタブは単にテスト対象が期待する挙動を返すものであるのに対し、モックはテスト対象が別のコンポーネントと期待したやりとりを行なっているかをチェックするためのもので、引数やメソッドの呼び出し回数の検証も行うことができるようです。

### テスティングフレームワーク

テスティングフレームワークとは、ソフトウェアテストの自動実行プログラムの作成、テスト結果の自動判定や集計などの機能を提供するソフトウェアフレームワークのことです。テストフレームワークと呼ばれることもあるようです。

PHP言語のテスティングフレームワークにはPHPUnitなどがあります。

### ベータ版

開発者以外による不具合のチェックのことをアルファテスト、一般ユーザによる不具合のテストをベータテストと呼ぶようです。

オープンソースソフトウェアを使用しているとベータ版という表現をよく見ますが、これはベータテストを目的に配布されたソフトウェアのことだそうです。ベータ版は、基本的には製品版と同等の機能を備えていますが、不具合が存在する可能性もあるため、利用には注意が必要です。

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

ブラックボックステストとは、E2Eテストなどのように、システムの内部構造は意識せず、仕様を満たしているかどうかのみをテストすることをいいます。主に複数のプログラムが組み合わさった機能テストやシステムテストで使われるようです。

ブラックボックステストのメリットは、システムを包括的に扱うため、コンポーネントを連携したときに初めて生じるような不具合も検知しやすく、想定漏れを防ぐことができる点です。バグが検知されたときに、どこに原因があるかはわかりづらいですが、一度にチェックできる範囲は広いです。

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

ホワイトボックステストとは、システムの内部構造を理解した上で、ロジックや制御の流れが正しいことをテストすることをいいます。関数やメソッド単位に実行される単体テストで主に使われるようです。

ブラックボックステストとは対称的に外部仕様を無視するため、仕様を誤解して実装した場合もテストを通過してしまうという点に注意が必要です。

ホワイトボックステストでは、テスト対象の命令や分岐条件をどれだけ網羅できているかを**カバレッジ**で表します。網羅率にも種類があり、すべての命令を網羅できているかを表す命令網羅、すべての可能な結果を網羅できているかを表す条件網羅、すべての判定のすべての可能な組み合わせを試せているかを表す複合条件網羅などがある。

### カバレッジ（網羅率）

カバレッジ（網羅率）は、全体のうちテストを実行した部分の割合のことです。

このとき、全体をどう定義するかによって、カバレッジが細分されるようです。

* コードカバレッジ：実行可能な命令の網羅率などソースコードに対する網羅率
* 機能カバレッジ：仕様に対するテストの網羅率
* データカバレッジ：とりうるデータに対するテストの網羅率

カバレッジの目標は、100%にならないことのほうが多いらしいです。

カバレッジの低いテスト実装の初期段階では、実装コストに比べてバグを検出できる確率が高い、つまり費用対効果が高いようなのですが、カバレッジが100%に近づくほど、この費用対効果は低くなるようです。おそらく、さきに重要な項目をテストするため、最後のほうは例外的で優先度も低く、実装も難しい条件が残ってしまう、ということだと理解しています。

ちなみにGoogleのカバレッジの努力目標は85%だそうです。

### リグレッションテスト

リグレッションテストとは、プログラムの一部を変更した際に、他の部分に不具合が発生していないかを確認するためのテストです。回帰テストや退行テストとも呼ばれるようです。

### デグレーション

デグレーションは、悪化や退化という意味で、プログラム修正時に他の部分で新たな不具合が発生することを指します。

デグレーションが起きていないか確認するのが、さきほどのリグレッションテストです。

### 動的テスト

動的テストは、実際にプログラムの実行を伴う動作確認やバグ検出、品質評価のことです。たんにソフトウェアテストというと、一般的には動的テストを指すようです。

### 静的テスト

実際にプログラムを実行する動的テストに対し、ソースコードを読み込んで構造を解析し、文法に誤りがないか、あらかじめ定義したコーディング規約に違反していないかなどを検証することを静的テストといいます。静的解析や静的コード解析とも呼ばれるようです。

PHPにはPHPStanなどの静的解析ツールがあり、引数の数や型の一致などを確認してくれます。

### ストレステスト

システムに通常以上の高い負荷を意図的にかけて、処理の低下やデータ破損、発熱といった影響が、どのような条件で発生するのかテストすることをストレステストあるいは耐久テストといいます。

高い負荷がかかった状況でしか発生しない不具合、負荷の大きさと比例しない急激な性能の低下などを検出することができます。

ソフトウェア開発でのみ使われる専門用語ではなく、たしか製薬でも使われていましたし、経済などの分野でも使われる一般的な用語のようです。

### ビッグバンテスト

ビッグバンテストとは、すべてのコンポーネントが完成してから、全体を結合させたプログラムを一気に検証する手法のようです。

テスト実装にかかるコストは低いですが、バグが検出されても原因箇所の特定が難しくなるため、大規模なソフトウェアには向かないといわれています。ビッグバンテストのまえに、各コンポーネントの単体テストは完了している必要があるそうです。

### トップダウンテスト（下降試験）

トップダウンテストは、ソフトウェアの上位モジュールから順に結合しながらテストしていく手法のことです。

このとき、下位モジュールは未実装もしくは未検証であるため、スタブと呼ばれるダミーの簡易的なプログラムで代替します。スタブを使用したテストが正しく動作した場合は、スタブを実際のモジュールに置き換えてテスト、と下位に向けてテスト範囲を広げていきます。

上位モジュールからテストをすることで、システムの中枢部分の致命的なバグを早期に検出できるメリットがあります。

### ボトムアップテスト（上昇試験）

ボトムアップテストは、トップダウンテストとは逆に下位モジュールから順に結合しながらテストする手法です。

上位モジュールの代わりに用いられる簡易的なプログラムをドライバといいます。最下位のモジュールからテストをしていくため、プログラミングと並行して実施しやすいようです。一方、システムの中枢部分のバグを発見するのが遅れたり、気づかないリスクがあるようです。

### V字モデル

V字モデルは、開発工程とテスト工程の対応関係を表現したモデルだそうです。

実際の図を見たほうがわかりやすいと思います。

> ![v-model-640x348.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/500854/70ef0877-65b7-5f30-9f0d-ed25fd67e9b6.jpeg)

> 
> [システム開発におけるV字モデルとは？W字についても解説](https://hnavi.co.jp/knowledge/blog/system_v-model/)

V字モデルを利用することで、その時点で何をテストすればよいのかが明確になります。

## CI（継続的インテグレーション）

Ciは、Continuous Integrationの略で、継続的インテグレーションとも呼ばれます。

コードの変更をリポジトリに頻繁にマージし、そのたびにビルドとテストを自動実行する手法です。複数の作業を並行することによりコンフリクトが発生したり、デグレーションが起こることはよくありますが、それらを早期に発見し、修正箇所を最小限に抑えることが可能になります。

### CI/CDパイプラインとは

CI/CDパイプラインの構築、といった表現もよく見ます。

複数の処理を直列につなぎ、まえの処理の出力が次の処理の入力になるような構成をパイプライン処理というようです。ですので、CI/CDパイプラインとは、ビルド、テスト、マージといった一連の処理を直列につないで自動化したもの、と私は理解しました。

### CIのメリット

CIを導入する以前はその必要性が理解しづらいけれど、一度CIを導入すると戻れなくなる、という文章を何度か見ましたが、私も実際に体験しました。コード変更のたびに手動でビルドしたり、デグレーションが起きていないか確認をするのは非常に大変ですし、なによりリスキーだという感覚になりました。

CIのメリットとして、次のようなものがあります。

* バグの影響範囲が小さいうちに検出できるため、修正にかかるコストを削減できる
* 常にビルドやテストが通る状態を維持できる
* テストにかかるコストを削減できる

### 効果的なCIのポイント

継続的インテグレーションを効果的にするためのポイントが書かれていたので紹介します。

* コードを頻繁にコミットする
* ビルドやテストが失敗したコードはコミットしない
* 開発者は単体テストを書く

すべてのビルドとテストが合格した状態を**オールグリーン**というようです。

一度でもエラーを無視してしまうと、エラーが出ていても気にしないようになり、バグを早期発見して修正コストを抑える、マスターブランチを常にエラーのない状態に保つ、といったCIの恩恵が得られなくなってしまいます。

## テスト設計の4つのポイント

私はテスト担当者でもなんでもないのですが、今回テストに関する情報を集めるなかで、テスト設計に役立ちそうな情報が得られたのでまとめたいと思います。

* 要件定義書の曖昧な表現を厳密なものに直す
  * 要件が曖昧だとテスト項目も曖昧になり、実装できなかったり、手戻りが発生したりする
* 要件定義書に漏れがないかチェックする
  * 要件定義担当者とクライアント間だけの暗黙の了解も、要件定義書に追加する
* テスト設計を要件定義担当者にレビューしてもらう
  * テスト観点の漏れを防ぐため、システムの仕様に最も詳しい要件定義担当者にレビューしてもらう
  * 細かいテストケースの一覧ではなく、最初に大筋が合っているかを確認してもらうことで漏れを防ぐ
* テストケースの偏りをなくす
  * 自分がよく理解している機能は、テストケースが多くなりやすい
  * テストケースが偏っていないか客観的に評価する