自動テストについて、考え方自体は5年以上前から知っていましたが、プロジェクトで実際に使われているのを見たのは今年4月になってからでした・・・
自動テスト完備なんて昔は夢物語だと思ってたんですけどね・・・
昔なぜ自動テストができないと思ったか(そして現在のプロジェクトはいかにそれを実現しているか)を列挙していきたいと思います。
ちなみに全テストを実行するまでに許容できる時間は5〜10分とします。
DBの初期化
一番はこれですね・・・今まで自動テストやるのに最大の関門はDB系のテストでした。
DBのテストをする場合、テスト用のDBを用意していたりしたんですが、どうしてもテストのたびにデータが変わってしまってテストの担保ができません。
時間経つとExcelを引っ張り出して最初からいちいち作り直しみたいなことをやってました。
DBのテストを実行する場合、理想的には関数ごとにテーブルデータ初期化(つまりはmigrationを都度行う)が必要になります。
ですので、migrationが必ず完備されており、テストのたびに新規のテーブルが作れればOKです。
これだけでテストができなかった7割ぐらいの問題は解決しました。
Laravelですとuse RefreshDatabase;というコマンドでテストのたびにmigration実行させることができます。
【Laravel】PHPUnitの実行でDBを汚染しないためにできる簡単なこと
テストデータ作成
DBの初期化とセットのような内容ですが、テストの場合、テストそのものよりはデータの用意に時間がかかることが圧倒的に多いです。
そのため、テストのたびに前提となるテストデータがすぐに作れるか。
そしてテストデータを作りやすいようにそれ用のメソッド(Laravelだとfactoryメソッド)が用意されていることが大前提になります。
データ量の制限
次に最低限の稼働をさせるDBの量がそもそも少ないというのが大事かと思います。
テストのたびに初期化をするのでその度に大量のデータを入れるようなものばかりだと時間が大幅にかかってしまいます。
マスタ系のデータでしたらできればレコード量を100以下にしないとそもそも時間がかかってしまって現実的でないと思います。
(どうしても大量データがある場合は通常時はskipさせるなどの処置が必要かと思います。)
外部連携
次に自動テストが難しいと思ったのは外部のサービスですね。具体的にはAPI連携やAWS系のサービスです(例えばS3など)。
今のプロジェクトでAPI連携はないのですが、これはDIを使い、ローカル時にはJSONファイルなどで対応させるのが一番かと思います。
AWS系のサービスではlocalstackなどテスト用の仮想環境を使うのがいいでしょう(phpunitをdockerで実行させることになりますが・・・)。
またそもそもラッピングしたメソッドを用意し、DIで代用しても良いかもしれません・・・
複雑なm×nパターン
バリデーションなどが一般的ですがパターン数が多くなる場合、それ用のメソッドを整備しないとコードの量がめちゃくちゃ増えちゃいます。
そこで、ケースを配列などで用意しておき、それを読み込む特定のメソッドなどを用意しておくと良いでしょう。
LaravelですとdataProviderという仕組みがあり、複数のパターンが用意に作れます。
テストコードを書くコスト
技術的なことではなく、政治的なことにもなりますが、そもそもテストコード自体を許可してもらえるか、ということです。
長い目で見れば少しの修正のたびに手動での確認というのは手間が天文学的になるので、カバレッジ100%を目指さなくてもざっくりとテストコードはあったほうがいいと思いますが、短期的には成果物の進捗にはマイナスになります。
そこで、プロジェクトでテストコードを書く手間を許容できるか・・というのがまずプロジェクトを始める上での最初の関門になるかと思います。