📝

単体テストのあれこれについての雑多なメモ

2023/04/09に公開

はじめに

単体テストの考え方/使い方を読んで色々書きたくなった点を雑多に記載してます。

テストを書くことのメリット

色々あるとは思いますが、個人的にテストを書くようになってよいと思ったのは以下点です。

  • テスト作成時にバグが見つけられる
    テストケースを書いていると、このケースだとダメなのではというのを見つけることがあります。
    また、動作は問題ないものの、条件分岐の順番やコードの見やすさなど、少し改善できる点を見つけられます。
  • テスト作成時によくない実装を正せる
    テストを書こうとしたとき、色々なものと依存してしまっていたり、1つのクラスが複数の責務を持ってしまって、テストが書きにくいということに気が付きます。
    ここでよくないということがわかるようになると、後々の設計実装時では事前に適切なクラス分離などをより意識できるようになります。
  • 機能拡張や変更時によりどころがある
    テストがあると、少し変更するにもデグレを引き起こしていないかといった心配が無い頃より圧倒的に少なくなります。

テストを書く粒度

実装しているロジックに対して、外部から観測可能な振る舞いに関して、ブラックボックスとしてテストを用意するのがよいかと思います。
以下イメージの場合、

  • INがテストコードでテスト対象の処理の呼び出し。
  • OUTが呼び出し結果のためこれを検証する。
  • 緑の処理Bは外部依存でテストダブルに置き換えが必要な処理の想定で、これが呼び出されたかも検証する。
    1

ホワイトボックステスト

ホワイトボックスで詳細なテストを書くと、作成時点ではよくても、少しの変更ですぐ壊れてしまうテストになってしまいます。
ホワイトボックスのテストについて、個人的にはユーティリティ的な関数で処理が複雑かつ、基本的に変更されないものに対して行っておく分にはいいのかなという考えです。

テストの作成について

AAAパターン

単体テストを作成する上での記述パターンです。
単体テストコードは「準備(Arrange)」、「実行(Act)」、「確認(Assert)」の3つのフェーズで記述。
コメントについては、本にも記載ありましたが、各フェーズが複数行になるなどの場合は記載した方がわかりやすくなります。
数行程度ならフェーズ毎に改行を入れる程度で十分理解できるためコメントは不要。

[TestMethod]
public AAAパターンのサンプルテスト() {
  // 準備  
  var stu = new SystemUnderTest();

  // 実行
  var result = stu.DoSomthing();

  // 確認
  Assert.AreEqual(result, "AAA");
}

テストダブル

単体テストは一般的に環境に依存せず、動作させられる必要があるかと思います。
そのため、外部通信など外的要因に依存する部分はテストダブルを使用して、テストを動かせるようにする必要があります。

テストダブルの種類

  • モック
  • スパイ
  • スタブ
  • フェイク
  • ダミー

古典学派とロンドン学派

単体テストには古典学派(デトロイト学派)とロンドン学派(モック主義者)の2つの考え方が存在します。

  • 古典学派
    外に対する依存部分に対してテストダブルを使用します。
    影響する他のクラスもそのままプロダクトコードを使用するため、テストの準備に手間がかかるかつ、失敗時にどの部分で失敗したのかがわかりにくくなる可能性があります。
    ただ外から見える振る舞いに対して適切にテストをしていれば、少々の内部の変更があってもテストが壊れることがなくなります。

  • ロンドン学派
    テスト対象のクラス以外の部分はテストダブルを用いるようにします。
    これにより常にテスト対象だけを検証することができ、依存する部分の順部など考えずにすむ
    ただし、依存部分の変更などですぐテストが壊れる可能性があります。

その他

最近MSが単体テストのベストプラクティスの記事を上げており、初学者にはまずこれを読んでもらうのがいいかもと思ったりしています。
いきなり本読んで勉強しろというのも無理があるので。
.NET Core と .NET Standard での単体テストのベスト プラクティス

参考

単体テストの考え方/使い方

Discussion