初めてのテストコードの書き方
初めてテストを書きたいと思ったとき、どこにどれくらいの量をどのように書くのか全くわからないでしょう。そのような人のために私なりのやり方を書いていこうと思います。
対象読者
対象読者はテスト自動化についてある程度知識を持っている方が対象です。もしテストにあまり詳しくないのであれば以下の記事を先に読むことをオススメします。
書いていないこと
各種テストライブラリに依存するので、細かいテストの実装内容は書いていません。
どのようなテストをすればいいか?
初めてのテストの場合は、まずユニットテストを書きましょう。
ユニットテストはテスト全体の 80%を占めるのでこれが書けなければ、自動化する意味をあまり感じられないです。動作が速いため何度も書き直して試すことができ、試行錯誤しやすのも初めに学ぶのに最適です。
インテグレーションテストや E2E テストを最初にやるのを私はあまりオススメしません。
インテグレーションテストや E2E テストは依存性などをあまり気にしなくてもいいので覚えやすいです。しかしユニットテストと比べて試行回数が減るのと、機能を網羅しようとして作りすぎると保守が大変になります。
その結果、便利になったというプラス体験より辛いというマイナス体験のほうが勝ってしまう可能性があります。
テストの対象は?
ユニットテストを書くとして、どのソースコードに対してテストを書くべきでしょうか?
そのシステムが扱っているアーキテクチャパターンによってテストする場所は変わります。しかし最適な場所の本質はどれも同じです。
本質は依存性を注入しやすく、フレームワークと分離しやすいコードをテストすることです。なかでもユースケースを扱っているコードが最適です。
速度と確実性が高くて保守もしやすく、忠実性もそこそこあり、テスト対象も多くない(少ないとは言っていない)のがポイント高いです。とてもコスパがいいです。
ただし、複雑な条件を扱ったメソッドは別途テストコードを書いたほうがいいです。
MVC
MVC の場合はコントローラー層が最適です。
MVC にはサービスレイヤーがないためコントローラーに対してテストを書くことになります。フレームワークをテストに含めてしまうため速度が少し遅くなったり、事前事後処理の扱いが面倒だったりしますが、これが最善だと思っています。
サービスレイヤーが増えた MVSC の場合は、S でユースケースを扱っているのであれば、そこが最適なテスト対象となります。
MVC だと Rails や Laravel のようにモデルがアクティブレコードである場合があります。その場合は依存性の注入が非常に難しくなります。
私はそのようなときは開発規模によって対応を変えたほうが良いと思っています。
開発期間が 3 ヶ月より短かく今後の機能追加や保守もない場合、単純な CRUD なシステムの場合は、依存性を受け入れてインテグレーションテストとして扱います。
開発期間が 3 ヶ月以上になる場合、将来的な機能追加も含めて 6 ヶ月以上になる場合、1 年以上の保守期間がある場合などはアクティブレコードを捨てて DDD で開発したほうがいいです。
ドメイン駆動設計
ドメイン駆動設計の場合はアプリケーションサービス層が最適です。
大抵の場合、アプリケーションサービスレイヤーはユースケースを扱っていて、フレームワークとも分離されているので作りやすいです。
たまにアプリケーションサービスレイヤーでユースケースを扱わないプロジェクトがあったりするので注意が必要です。
クリーンアーキテクチャ
クリーンアーキテクチャの場合はユースケース層が最適です。
理由に関してはドメイン駆動設計と同じです。
テストの置き場所は?
テストの置き場所に関しては、意見が色々割れます。言語的な理由で置き場所が制限される可能性もあります。
一般的には以下の 3 つの候補があります。
- src ディレクトリと同じ階層に
test
ディレクトリを作る - テスト対象のファイルが有るディレクトリ内に
__test__
ディレクトリを作る - テスト対象のファイルが有るディレクトリにそのままテストファイルを入れる(.spec.ts のような拡張子になる)
私は 3 番目のやり方が好みです。
理由
- 対象ファイルの近くにあるのでファイル間の移動が楽
- どのファイルに対してテストを行っていないかがひと目で分かる
- コードレビュー時に実装コードとテストコードが近くに表示されるのでレビューしやすい
書くときの作法は?
特にこうしなければいけないというものはないですが、ケント・ベックが提唱したテスト駆動開発では以下のフローで開発します。
テスト駆動開発のフロー
- 対象機能のテストを変更または新規としてざっくりと書く
- テストを実行する(実装コードがないのでエラーになることを確認)
- 実装を書きながら、テストも書いてテストを動かす(完了速度重視でかなり汚く書いて良い)
- 実装を完了しテストをすべて成功にする
- リファクタリングしながらテストを動かす
- リファクタリングを完了しテストをすべて成功にする
さいごに
初めてのテストでの勘所はひととおり書けてると思います。
あとはたくさん書いて慣れていくことが大事です。
テストコードなしでは開発できない体になったら、インテグレーションテストにも挑戦してみましょう。
Discussion