単体テストの考え方/使い方を読んで[3]
概要
単体テストの考え方/使い方をほぼ読み終わったので、テストコード1年経験者が学びになったポイントをかい摘んで書き残してみたい。
本全体の所感については[1] に記載。
学びになったポイント
備忘録的に学びになったポイントを残しておきたい。
なぜテストを書くのか
プロジェクトの成長を持続可能なものにするため。コードベースは成長とともに劣化していき、リファクタリングなど適切に保守しないと無秩序になっていく。質の良いテストを用意することで、変更によって発生する退行(regression)を検出出来るようになり、開発時のセーフティネットとなる。テストコードは負債
テストコードはプロダクションコードと同じように資産ではなく負債として見るべきであり、よって価値をもたらさないテストは作成しない。前提として価値をもたらす質の良いテストとそうでないものを見分けられる必要がある。なぜ古典学派を支持するか
ロンドン学派の検証方法は実装の詳細と密接に結びついてしまい、リファクタリング耐性に乏しい。また得られるメリットも古典学派のやり方でも十分に得られ、古典学派の方が協力者オブジェクトを含めた1つの振る舞いを検証でき、リファクタリング耐性を持った、より深いテストが行える。カプセル化とは
不変条件の侵害が起こらないように保護する設計。1単位の振る舞いを適切に実行出来るよう、データと振る舞いをまとめることで不変条件を常に守る完全性を担保する。リファクタリング耐性とは
振る舞いの結果を変えない修正を行なった際のテストの壊れにくさ、壊れないこと。耐性が低いと、プロジェクトで都度発生する開発時の実装の変更やリファクタリングを行った際に高頻度でテストが壊れ、保守工数がかさんでしまう。保守しない場合は偽陽性と呼ばれる不必要な失敗を放置することになり、テスト結果自体の信用性、テストコーディングのモチベーションを下げることに繋がる。テスト検証の対象
問題領域にとって最終結果となる外部から観察可能な結果のみを検証の対象とする。すべてのテスト(単体、統合、E2E)においてブラックボックステストとして検証すべき。つまり外部に出力するためのコミュニケーションを模倣する「モック」は最終結果の一つとして検証対象となり、内部の入力に用いるための「スタブ」は実装の詳細となり検証対象としない。出力値ベーステストとは
テスト対象に隠れた入力、出力が無い状態で、与えた入力に対して出力された値を検証するテスト。実装の詳細と結びつくことが少なく、質の高いテストケースが作成出来る。関数型プログラミングとは
隠れた入力(内部でDBやファイル参照など)や出力(副作用)を持たない関数で非常にテストが行いやすい。ビジネスロジックと副作用を分離することを目的として導入する。プロダクションコードの4分類
コードの複雑さや重要性、協力者オブジェクトの数の観点から4分類できる。このうちドメインモデルやアルゴリズムについては単体テストとし、振る舞いを組み立てるコントローラは統合テストとなる。取るに足らないコードについては価値をもたらさないためテストを用意すべきではなく、過度に複雑なコードについてはドメインモデルとコントローラの責務を適切に分けるようリファクタリングをすべき。ドメインイベントとは
ドメインモデルで発生するビジネス上重要な状態の変更。変更を追跡できるようにする責務をドメインモデルに持たせることで、コントローラの複雑さを取り除けるようになる。統合テストとは
単体テストではないテスト。プロセス外依存と統合した状態でどのように機能するのかを検証する。
平たくコントローラを検証する。単体テストはドメインモデル、アルゴリズムを検証する。
管理下にある依存(DB)はモック化せず、依存を扱う層をすべて経由する検証を行えるようにし、管理下に無い依存(外部API)にモックを使い、そのコミュニケーションを検証する。インターフェースによる抽象化の妥当性
実装クラスを1つしか持たないインターフェースによる抽象化は不要。テストの際にモック化するために使用する妥当性があるのは管理下にない依存に対してであり、そうではなく使用されている場合は実装の詳細を検証しようとするような、設計に問題を抱えている可能性がある。テストでの検証に用いる値やロジックについて
プロダクションコードに定義されたリテラルや定数を使わず、必要に応じて複製する。そうしないと実質的に何も検証していないテストケースになってしまう。
アルゴリズムやロジックについても同様でドメイン知識がテストに漏れることを避けるよう、ブラックボックステストの観点でテストすべき。モックの使用について
モックは管理下にない依存だけにしか使わない。管理下にない依存を扱うのはコントローラのため、統合テストを行うときだけとなる。
言い換えると単体テストではモックを使ってはならない。データベース、リポジトリのテストについて
書き込みは必ずテストするが、読み込みは重要な読み込みだけをテストし、そうではないものはテスト対象外とする。
リポジトリのテストは保守コストが高く労力に見合わないため行わない。統合テストのシナリオの一部に含ませて間接的に検証されるようにする。
あとがき
特に有用と感じられたものについて太字にした。
古典学派のスタイル、価値をもたらさないテストは作成しない、最終結果以外は検証しない、単体テストではモックを使わない、など大きな指針となった。テストコーディングの書き方をきちんとアップデートしていきたい。
この記事が気に入ったらサポートをしてみませんか?