見出し画像

ステートテスト VS インタラクションテスト

くぼぴー / note inc.

こんにちは、kubopです。
Googleにはトイレテスト(TotT)という文化があるようで、テストに関するTipsをトイレに貼り出し、テストに関する知識を全社で共有しているらしいです。
昨今はリモート勤務が広まり、TotTの実施は難しく、SlackやBotを用いてもなかなか浸透するかどうか…
そこで、noteに書きつつ自分が勉強するために、少しずつ読んで内容や、所感を書いてみようと思います。
※ 翻訳・解釈の間違いなどあるかもしれません。
その場合はこっそり教えてください。

もっとテストを書いてほしいのです。そう、あなたです。テストは、コードをリファクタリングするときや他の開発者が機能を追加するときに、 あなたを守るセーフティネットであることはもうおわかりでしょう。また、テストがコードの設計に役立つこともご存知でしょう。

私たちは、この秘密兵器(this secret weapon)を世界中の人々と共有し、私たちのテストへの情熱を広めるとともに、あなた自身やあなたの会社の他の人たちに、この重要なトリックやテクニックを楽しく簡単に学んでもらうことにしました。
このブログで定期的にエピソードを紹介し、PDFを提供しますので、プリントアウトしてご自分のバスルーム、廊下、キッチン、月面基地、秘密の地下要塞、億万長者の創業者のプリウスなど、どこにでも貼り付けてください。

Introducing "Testing on the Toilet"

Testing on the Toilet: Testing State vs. Testing Interactions

ステートテストとは、テスト対象のコードが正しい結果を返すかどうかを確認することです。

public void testSortNumbers() {
  NumberSorter numberSorter = new NumberSorter(quicksort, bubbleSort);
  // 返されたリストがソートされていることを確認する。
  // 正しい結果が返される限り、どのアルゴリズムが使用されるかは重要ではありません。
  assertEquals(
      new ArrayList(1, 2, 3),
      numberSorter.sortNumbers(new ArrayList(3, 1, 2)));
}

インタラクションテストとは、テスト対象のコードが特定のメソッドを正しく呼び出すかどうかを検証することです。

public void testSortNumbers_quicksortIsUsed() {
  // クラスにモックを渡し、テスト対象のメソッドを呼び出す。
  NumberSorter numberSorter = new NumberSorter(mockQuicksort, mockBubbleSort);
  numberSorter.sortNumbers(new ArrayList(3, 1, 2));
  // numberSorter.sortNumbers()がクイックソートを使用したことを確認する。
  // このテストではmockQuicksort.sort() が一度もコールされていない場合、
  // あるいは間違った引数でコールされた場合 (例: mockBubbleSort が数字の並べ替えに使用された場合) は失敗するはずです。
  verify(mockQuicksort).sort(new ArrayList(3, 1, 2));
}

2 番目のテストはコードカバレッジはよいのですが、ソートが正しく動作しているかどうかはわかりません。
インタラクションテストがパスしたからといって、コードが正しく動作しているとは限りません。
このため、多くの場合はインタラクションではなく状態をテストすることになります。

一般に、インタラクションテストすべきなのは、 正しさがコードの出力内容だけでなく、その出力がどのように決定されるのかにも 依存している場合です。
上の例では、クイックソートが使われていることが重要な場合(たとえば、別のソートアルゴリズムを使うとメソッドの実行速度が遅くなる)だけ、ステートテストに加えてインタラクションテストも行いたいと思うでしょう。

インタラクションテストを行いたい例として、どのようなものがあるでしょうか…?

  • テスト対象コードがメソッド呼び出し時に順番が異なると副作用が起こる場合。

    • メールを一通だけ送りたい。

    • 特定の数のディスク読み取りをしたい。

    • 間違った順番でメソッド呼び出しをするとデッドロックする。

  • UIレンダリングの詳細がUIロジックから抽象化されている場合。

    • MVCやMVPを利用している場合など。

    • ビューの特定メソッドがコールされたことだけを気にするため、実際に何がレンダリングされたかは気にしないため。

    • 同様に、ビューをテストする際に、コントローラ/プレゼンターとのやりとりをテストすることができます。

🤔

おそらく、最も定石となるのはステートテスト、つまり状態、APIからのレスポンスが正しいか否かをテストするのが最も良いのだと思うのですが、
例えばメール配信が同時に含まれているAPIメソッドである場合は、メール配信メソッドがコールされたかを確認する必要がある。ということなのかなと思います。
分岐でメール配信するか否かの場合は、レスポンスではなくてメール配信メソッドのコールが鍵になると思います。

Licensed under a Creative Commons
Attribution–ShareAlike 4.0 License


この記事が気に入ったら、サポートをしてみませんか?
気軽にクリエイターの支援と、記事のオススメができます!
くぼぴー / note inc.
note/サーバサイドエンジニアです。 ここ最近はQA活動をしております。 哲学とかそういうのが好きです。 何か新しい考えが浮かんだら記事を書いていきたいです。