Suite単位でLaravelテストを整理してみた
Laravelのテストって、最初は Feature と Unit に分ければいいと思ってたんですよね。
でも実際にプロジェクトが育ってくると、「このテスト、どっちでもないな…」が増えてくる。
そこで最近は Suite(スイート)単位 で整理するようにしてみました。
目次
Feature と Unit の分け方が限界にきた話
Laravel標準の構成ってこうですよね。
tests/
├── Feature/
└── Unit/最初はこれでいいんですが、DDD構成を取り入れると
「Application層のテスト」や「InfrastructureのDBクエリテスト」など、
どっちにも入れにくいテスト が増えてきます。
結果、「とりあえずFeatureに入れるか…」となって、
あとから探しづらくなるんですよね。
Suite方式とは
Suite方式は、Laravelのレイヤ構成そのままにテストを並べるやり方です。
tests/Suite/
├── Domain/
├── Application/
├── Infrastructure/
│ └── Queries/
└── Presentation/それぞれが1つのテストスイート(まとまり)として機能します。phpunit.xml の <testsuite> タグを使えば、レイヤ単位で実行も可能です。
phpunit.xml の設定例
<testsuites>
<testsuite name="Domain">
<directory suffix="Test.php">./packages/Tool/General/tests/Suite/Domain</directory>
</testsuite>
<testsuite name="Application">
<directory suffix="Test.php">./packages/Tool/General/tests/Suite/Application</directory>
</testsuite>
<testsuite name="Infrastructure">
<directory suffix="Test.php">./packages/Tool/General/tests/Suite/Infrastructure</directory>
</testsuite>
<testsuite name="Presentation">
<directory suffix="Test.php">./packages/Tool/General/tests/Suite/Presentation</directory>
</testsuite>
</testsuites>これで、例えばこんな感じで実行できます。
php artisan test --testsuite=Application
php artisan test --testsuite=Infrastructureレイヤごとの独立実行ができるので、
「Infraだけ重いから時間あるときに回そう」みたいな運用もしやすいです。
命名ルールは「機能+対象+Test」
テストクラスの命名も少し揃えました。
| 層 | 例 | 内容 |
|---|---|---|
| Domain | SpotEntityTest | 値オブジェクト・ドメインルール |
| Application | SearchSpotUseCaseTestSpotControllerTest | UseCase単体 |
| Infrastructure | DbSpotListQueryTest | DBや外部I/O |
| Presentation | SpotControllerTest | Controller経由の動作確認 |
どこを見ても「何をテストしているか」が分かるようにしています。
アトリビュートで粒度を明示
PHPUnit 10〜では、アノテーションではなくアトリビュートが使えます。
これを活かして、テストの規模感をラベル化しています。
use PHPUnit\Framework\Attributes\Small;
use PHPUnit\Framework\Attributes\Medium;
use PHPUnit\Framework\Attributes\Large;
#[Small] // 完全にユニットテスト
#[Medium] // UseCaseやDTOなど、少し外部に依存
#[Large] // DBやファイルを使うどんなテストかがコード上で一目でわかるのが地味に便利。
Stub / Fake / TestData の置き場
tests/Suite/Support/Models の下にテスト用モデルを置くようにしました。
このモデルでFactoryを読み出すようにしています。
tests/Suite/Common/
└── Support/
└── Models/
├── StubArea.php
├── StubSpot.php
└── StubNews.php命名は「StubAreaSection」のように接頭辞型に統一。
ファイル一覧でパッと見て「テスト用だ」とわかるのがポイントです。
実際にやってみて感じたこと
- 探すのがめちゃくちゃ楽になった(UseCaseがどこか迷わない)
- テストの重さがレイヤごとに見える
- CIでSuite単位にジョブを分けられる(Domainは毎回、Infraは夜だけなど)
- Feature/Unitという分類のモヤモヤが消えた
今の運用ルールまとめ
| 項目 | ルール |
|---|---|
| ディレクトリ構成 | /tests/Suite/<層名>/... |
| クラス命名 | <対象><役割>Test |
| 粒度 | #[Small] / #[Medium] / #[Large] |
| テストデータ | /tests/Suite/Common/ に Stub/Fake |
| 実行単位 | Suiteごとに php artisan test --testsuite= |
まとめ
LaravelでDDDっぽい構成をやるなら、
テストも「Feature / Unit」じゃなくて「層」で考える方が自然です。
Suite単位に整理すると、どこに書くか迷わないし、CIも分けやすい。
なにより「プロジェクトの構造とテストの構造が一致する」ので、
コードを読む人がすぐに全体像をつかめると思います。
今回は以上です!