Laravelのテスト、どこに何を書けばいいの?初心者向けに整理してみた
はじめに
Laravelを学び始めたとき、こんな疑問が出てくることがあります。
「テストって書いた方がいいのはわかるけど、どこに何を書けばいいの?」
この記事では、Laravelのテストの種類から、どのクラスに何を書くべきかまでを整理していきます。ReactやNext.jsを触ったことがある人にも比較しながら説明するので、ぜひ参考にしてみてください。
Laravelのテストは2種類ある
Laravelでは、主に2種類のテストを書きます。
ユニットテストは、個別の関数やメソッドが正しく動くかを確認するテストです。たとえば「calcTax()メソッドが正しい税込み金額を返すか」といった粒度でテストします。
フィーチャーテストは、アプリケーション全体の機能が正しく動くかを確認するテストです。たとえば「ユーザー登録のエンドポイントにリクエストを送ったとき、DBにユーザーが作成されるか」といった確認をします。
JSTQBを知っている人向けに補足すると、ユニットテストはコンポーネントテスト(単体テスト)、フィーチャーテストは統合テストに近いイメージです。
なぜLaravelだとユニットテストのイメージが湧きにくいのか
ReactやNext.jsで開発していると、ユニットテストは比較的イメージしやすいと思います。
コンポーネントという単位が自然に存在していて、「この値を渡したらこう描画される」という入出力が明確だからです。
// propsを渡して → JSXが返ってくる。テストしやすい!
<UserCard name="田中" />
一方でLaravelは、ControllerやModelがDBと密結合しがちで、「どこを単体として切り出せばいいか」が見えにくいです。
ここで意識するべきなのは、「DBやHTTPに依存しない純粋なロジックがあるか?」 という視点です。
どこにロジックを書くべきか
Laravelには複数のクラスがありますが、それぞれの責務はこう考えると整理しやすいです。
| クラス | 責務 |
|---|---|
| Controller | HTTPリクエストの受け取りとレスポンスの返却 |
| Model | DBとの関係・リレーション・アクセサ |
| Serviceクラス | ビジネスロジック(ここがユニットテストの対象!) |
たとえば「名前に「様」をつけて返す」という処理を考えてみます。
❌ Controllerに書いてしまうケース
public function show(Request $request)
{
$name = $request->name . '様'; // ← ここのロジックはテストしづらい
return view('user.show', compact('name'));
}
Controllerに書いてしまうと、このロジックをテストするためにHTTPリクエストまで用意しないといけなくなります。フィーチャーテストになってしまい、テストが重くなります。
✅ Serviceクラスに切り出すケース
// app/Services/UserService.php
class UserService
{
public function formatName(string $name): string
{
return $name . '様';
}
}
// Controllerはサービスを呼ぶだけ
public function show(Request $request)
{
$formatted = $this->userService->formatName($request->name);
return view('user.show', compact('formatted'));
}
こうすると、formatName() だけをシンプルにテストできます。
// Tests/Unit/UserServiceTest.php
class UserServiceTest extends TestCase
{
public function test_format_name_adds_honorific()
{
$service = new UserService();
$result = $service->formatName('田中');
$this->assertEquals('田中様', $result);
}
}
DBもHTTPリクエストも関係なし。純粋なロジックだけをテストできています。
Fat Modelには気をつけよう
「じゃあModelに書けばいいか」と思った方、ちょっと待ってください。
Modelにロジックをどんどん書いていくと、Fat Model(太ったモデル) という有名なアンチパターンに陥ります。
Modelはどんどん肥大化しやすく、気づいたときには500行を超えるModelファイルができあがって、何がどこにあるかわからない状態になってしまいます。
ただし、Laravelのアクセサという仕組みを使う場合はModelに書くのが適切です。
// そのモデル固有の属性変換はModelでOK(アクセサ)
protected function formattedName(): Attribute
{
return Attribute::make(
get: fn() => $this->name . '様'
);
}
アクセサは「このモデルの属性をどう見せるか」という責務なので、Modelに書いても問題ありません。
ビジネスロジック(計算・変換・判定など)はServiceクラス、モデル固有の属性変換はアクセサ、というように使い分けるのがベストプラクティスです。
ユニットテストの書き方の基本
ユニットテストはAAAパターンで書くと分かりやすくなります。
public function test_format_name_adds_honorific()
{
// Arrange(準備):テストに必要なインスタンスを用意する
$service = new UserService();
// Act(実行):テスト対象のメソッドを呼び出す
$result = $service->formatName('田中');
// Assert(検証):期待した結果になっているか確認する
$this->assertEquals('田中様', $result);
}
Arrange → Act → Assert の順番で書くと、テストの意図が読みやすくなります。
まとめ
- Laravelのテストはユニットテストとフィーチャーテストの2種類がある
- ユニットテストはServiceクラスのメソッドを対象にすると書きやすい
- ControllerにロジックをつめこむとテストがHTTPリクエストごと必要になってしまう
- ModelにロジックをつめこみすぎるとFat Model問題になる
- ビジネスロジックはServiceクラスに切り出すのがベストプラクティス
「Serviceクラスにビジネスロジックを書く」という意識を持つだけで、設計もテストも自然と整理されていきます。ぜひ試してみてください!
Discussion