かっこうのブログ

何かしら飲んでるエンジニア

「単体テストの考え方/使い方」テストをドキュメントにしたい

adventar.org

アドカレ13日目!折り返しですね。日付がずれているのはご愛嬌。

本書は以下の項目を満たす理想のテストコードの書き方を考える本である。そのために様々なテストの手法やテストのしやすいアーキテクチャリファクタリングの手法についても記載されている。

一方で、悲しいかなこれらを全て満たす銀の弾丸は存在せず、様々な天秤についても語られている。

テストにも、プロダクトや組織の特徴を理解する必要性があるのだと感じられる一冊。

テストと設計の関係性

テストが書きやすいコードは密結合ではないコードである。そのため、テストを書くことでプロダクトコードは疎結合にせざるをえなくなる。

実際に本書でもテストコードを書くにはリファクタリングが必要であり、ヘキサゴンアーキテクチャの紹介などもされている。

一方で、これは副産物的なものでしかない。テストを書く=疎結合になる。だけで、設計が完全に良くなるわけではない。とは言いつつも、テストコードを書くことで関心の分離などに思考が向きやすくなるためプロダクトコードの可読性はグッと上がるだろう。

特に、テストを書く時に「出力値ベースのテスト棚」「状態ベースのテストだな」と認識できるだけでプロダクトコードに対しての認識も変わってきそうなところ

AAAパターン

テストコードを以下の3要素に分けて、可読性を向上させる手法です。

実際に実装するとこのような形になります。

class UserBooking extends TestCase
{

    public function ユーザーが予約する(Booking $bookingDate, bool $expected): void
    {
        //Arrange
        $user = new User(1);

        //Act
        $actual = $user->cancelBooking($bookingDate);

        //Assert
        $this->assertSame($expected, $actual);
    }
}

可読性だけでなく、Actに該当する要素が複数あるとカプセル化に失敗しているなど、プロダクトコードや設計を見直す機会になるのも魅力。

これを読んだ時真っ先に思い浮かんだのがBDDだ。BDDはTDDから派生したテストパターンの1つで、ユースケースに注目しておりAAAパターンと近くテストコードを以下の3つに分割しています。

BDDは統合テストで使うことが多いとされており、要件やユースケースをテストする際に使い、AAAパターンは単体テストという使い分けになる感じですかね。

  • Given:最初の文脈(前提)があって、
  • When:イベントが発生した場合、
  • then:なんらかのアウトプットを保証する。

digitalsoul.hatenadiary.org

テストでは結果のみを確認する

テストの流派には古典派・ロンドン派の2つの派閥があり、ロンドン派はモックを多用する。

このモックが「プロダクトコードの振る舞い」をテストしている場合、振る舞いを変えた時にテストコードがエラーになるようになる。こうなるとリファクタリングで結果は同じでも、プロダクトコードの振る舞いが変わっている場合テストが落ちるようになる。

これは望ましい状態ではなく、モックを使う時は外部サービスの呼び出しなどに留め、テストでは「プロダクトコードの果たした結果」に注目すべきという考え方だ。

テストコードの命名

これについてはAAAパターンやBDDのようにユースケースを日本語で命名するのが良いと思っている。

本書でも近く、面白いと思ったのは「テスト対象や協力者メソッドなどをテストメソッド名に含める」というもの。このような命名をしている時は、モックで振る舞いをテストしようとしている可能性が高いため見直すべきだと言う。

また、英語でShoudを使うなということも言われているが根本的に日本語を使えば問題はない。さて、なぜ日本語化なのだが、日本語の方が理解がしやすい・テストコードくらいエンジニアではないビジネスサイドでも意味がわかる状態であるべきという持論があるからである。

一方で、最近はChatGPTでテストを自動生成をする時は英語のほうが便利という話を聞く。ただ、この場合はカバレッジのみをとった形になるため、そもそもこのようなテストを書くべきではないだろう。