🙆

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