LaravelでTDDしていますか?
Laravelのプロジェクトを新規作成すると、tests/Feature, tests/Unitディレクトリがついてきます。
tests/Featureの下にはFeatureテスト、tests/Unitの下にはユニットテストを書いてほしそうな構成です。
では、あなたのユニットテストが継承しているTestCaseクラスは Tests\TestCase
でしょうか、それとも PHPUnit\Framework\TestCase
でしょうか?
もちろん、 Tests\TestCase
自体が PHPUnit\Framework\TestCase
を継承しているので、 extends Tests\TestCase
なテストであっても PHPUnit\Framework\TestCase
を継承はしています。
Tests\TestCaseはsetUp()でLaravelのapplicationを起動しています。
Laravelのapplicationが起動されていることによって、テスト時もFacade等の便利な機能が使えるというメリットがあるわけですが、一方でデータベースアクセスがあるクラスをテストするとテスト時にもデータベースを使ってしまうというデメリットがあります。
こうなると、テスト対象のクラス内には問題がなくてもDBデータやDBサーバーの状態によってテストが不安定になってしまいます(落ちやすくなる、結果が一定でなくなる)。
ユニットテストは「PHPUnitを使って書いたテスト」ではなく、「1つのクラスだけ独立して動かせるテスト」になるように書きましょう。
そして、ユニットテストを独立したテストとして書くためにはテストコードでなくプロダクションコード(テスト対象となるコード)の側を、ユニットテストできるように配慮して書く必要があります。
具体的には、 ユニットテストを書きたいぐらい複雑なクラスに対しては
- Laravelの提供するFacadeを使わずにFacade rootにあたるinterfaceをDIして利用する
- Modelのstaticメソッドを直接使わずサービスクラスを挟む(Repositoryパターンにしても良い)
のような工夫をすると良いです。
ユニットテストではモックした分、FeatureTestでは、ユニットテスト済の自分のクラスがデータベースやLaravelの機能と統合されたときに意図通りに動くかをテストできるように書きましょう。
その他tips
Laravel特有の配慮でなく、一般的なPHPコードに対するユニットテストを書きやすくする工夫については、PHPカンファレンス北海道2019のアンカンファレンスで話した テストを助けに使って設計を改善しよう~リファクタリングことはじめ~ にまとめていますので興味のある方はあわせてご覧ください。