
結論を急ぐ必要はない。LaravelにおけるPestとPHPUnitの違いは、完全に別のテスト基盤を比較する話ではないからだ。PestはPHPUnitを土台にしたテストフレームワークであり、Laravelのテスト機能やアサーションを捨てて乗り換えるものではない。主な差は、テストの書き方、補助機能、開発体験、そしてチームが既存資産をどう扱うかにある。
プロダクトグロースを預かる立場で見るなら、最初に確かめたいのは書き味の好みではない。チームの開発速度、テスト資産の保守コスト、新しいメンバーがテストを理解するまでの時間。この三つに対して、Pestが本当に改善をもたらすのかを仮説にしてから、移行の範囲を決めるべきだ。
PestとPHPUnitは敵ではない。PestはPHPUnitの上に成り立つ。対立軸で語るほど、判断は遅くなる。
PestとPHPUnitは「代替」ではなく「拡張」に近い
まず、仕組みの前提をそろえておきたい。
PHPUnitは、PHPにおける長年の標準的なテストフレームワークだ。テストケース、アサーション、データプロバイダー、モック、テストスイートなど、テストに必要な基本機能を広く持っている。Laravelのテスト機能も、最終的にはPHPUnitの仕組みを利用して実行される。
Pestは、そのPHPUnitを内部で利用しながら、より簡潔な記法やテスト整理のための機能を提供する。Pestで書いたテストも、実行時にはPHPUnitの仕組みと接続される。したがって、Pestを導入したからといって、PHPUnitのアサーションやLaravelのテストヘルパーが突然使えなくなるわけではない。
両者の関係を整理すると、次のようになる。
| 比較項目 | PHPUnit | Pest |
|---|---|---|
| 基盤 | PHP向けのテストフレームワーク | PHPUnitを土台にしたテストフレームワーク |
| 基本的な記法 | テストクラスとテストメソッドが中心 | クロージャーとit()、test()が中心 |
| アサーション | $this->assertSame()など | expect()による記法も利用可能 |
| Laravel連携 | Laravel標準のテスト機能で利用可能 | Laravel用プラグインで自然に統合 |
| 既存資産 | そのまま維持しやすい | PHPUnit形式との共存が可能 |
| 移行時の主なコスト | なし | ファイル構成、セットアップ、チーム習慣の調整 |
| 向いている場面 | 大規模な既存資産、標準性、細かな制御 | 新規開発、読みやすさ、テスト記述の摩擦低減 |
この違いから分かるように、Pestの導入は「PHPUnitから別の実行エンジンへ移る」よりも、「PHPUnitの上に別の書き味を加える」ことに近い。もちろん、プラグインや設定による差はある。しかし、移行の判断で最初から「今までのテストをすべて捨てる」と考える必要はない。
ここを誤解すると、議論が「Pestは速い」「PHPUnitは古い」「Pestはモダン」といった印象論に寄ってしまう。テストの実行速度は、フレームワーク名だけで決まらない。データベースの初期化、外部サービスのモック、カバレッジ計測、PHPの実行環境、PCOVやXdebugの設定、並列実行の可否など、複数の要素が影響する。
Pestへ移行しただけで、同じテストが自動的に高速化するとは限らない。速度を目的にするなら、どの処理がボトルネックになっているのかを先に確認するべきだ。ここを曖昧にしたまま移行すると、構文は変わったのにCIの待ち時間は変わらない、という結果になりやすい。
クロージャー構文がテストの読み方を変える
Pestの分かりやすさは、テストの構文に表れる。
PHPUnitでは、一般的にテストクラスを定義し、その中にテストメソッドを記述する。テストの準備や後処理は、クラスのセットアップメソッドと組み合わせて管理することになる。これは明確で、IDEや静的解析ツールとの相性もよい。一方で、テストの本体に入るまでにクラス宣言やメソッド宣言が必要になるため、短いテストでは周辺の記述が目立つことがある。
Pestでは、it()やtest()の中にテストの処理を直接書ける。テストの意図を、自然な文章に近い形で表現しやすい。たとえばユーザー登録を検証するテストなら、テスト名と処理が同じ視線の中に並ぶ。クラスの構造を追わなくても、どの操作を行い、何を期待しているのかを把握しやすい。
この差は、テストが少ないうちは単なる好みとして扱える。しかし、テストファイルが増えてくると、レビューや調査の負担に影響する。テストが仕様書の一部として機能するなら、実装者以外が読んだときの理解しやすさは無視できない。
たとえば、次のような要素が一つの流れとして確認できる。
- どのユーザー操作を対象にしているか
- どの条件で処理を実行するか
- データベースやメール送信に何を期待するか
- 失敗時に、どの仕様が破られたと判断できるか
Pestは、この流れをテストファイルの上から下へ読み進めやすくする。テスト名が単なる識別子ではなく、仕様の説明として目に入りやすいからだ。
ただし、読みやすさは構文だけで完成しない。テストの中に複雑な準備処理が詰め込まれていれば、Pestで書いても読みづらい。データ生成、認証状態の準備、外部サービスの差し替え、複数の分岐を一つのテストに入れれば、クロージャー構文の簡潔さはすぐに埋もれる。
Pestへ移行することで改善できるのは、テストの表面にある摩擦だ。テスト設計そのもの、責務の分割、テストデータの扱い、命名の一貫性まで自動的に整うわけではない。
expect()の利点は、短さよりも意図の見え方にある
Pestでは、expect()を使った期待値の記述も特徴になる。PHPUnitのアサーションでは、アサーションメソッドの種類を先に選び、対象の値を引数として渡すことが多い。Pestの期待値記法では、対象を起点にして、どの状態を期待するのかを続けて書く。
この書き方がすべての開発者にとって優れているとは限らない。しかし、テストの文章を上から読むという点では相性がよい。値を確認し、その値に対する期待を続けて表現できるため、アサーションの意図が追いやすくなる。
一方で、チェーンが長くなりすぎると逆効果だ。ひとつの値に対して多くの条件を連結すると、短く書けても失敗箇所の意味が曖昧になる。テストの可読性を高めたいなら、記法の短さではなく、一つのテストが一つの振る舞いを説明できているかを優先したい。
また、既存のPHPUnitアサーションをPestの記法へすべて置き換える必要もない。Pestのテスト内で、LaravelやPHPUnitのアサーションを使い続けることはできる。チームが慣れている記法を残しながら、新しいテストで少しずつPestらしい書き方を取り入れる方法もある。
移行時には、次のような順番が現実的だ。
1. 新しく追加するテストだけで、Pestの基本記法を試す。
2. チーム内で命名、セットアップ、モックの書き方をそろえる。
3. 読みやすさが明確に改善する種類のテストから、既存資産を変換する。
4. 変換後のテストが、元のテストと同じ振る舞いを検証しているか確認する。
5. 記法の統一よりも、機能開発への影響が大きくなった時点で作業を止める。
最後の判断が重要だ。移行では、コードがPest形式になったかどうかを成果にしやすい。しかし、プロダクトにとって重要なのは、テストが読みやすくなったか、壊れた仕様を見つけやすくなったか、開発者がテストを書くようになったかである。形式の統一が目的になると、移行そのものが新しい保守負債になり得る。
Pestの価値は、テストを短くすることではない。テストが何を保証しているのかを、コードの流れの中で読めるようにすることだ。
Laravel 11の標準構成は、移行を急がせる材料ではない
Laravel 11以降の新規プロジェクトでは、Pestを選びやすい構成になっている。新しいプロジェクトを始めるときに、チームがPestを選択すること自体は合理的だ。初期段階では既存テストがなく、テスト記法の方針を決めやすい。将来、PHPUnitからPestへ移行する作業を避けられるという意味でも、新規開発との相性はよい。
ただし、Laravel 11の初期選択を見て、既存のLaravelプロジェクトまで急いで書き換えるべきだと考えるのは早い。Laravelのバージョンアップとテストフレームワークの移行は、別の判断として扱った方が安全だ。
既存プロジェクトには、テストコードだけでなく、CI設定、開発者向けの手順、テストの自動生成コマンド、静的解析、カバレッジ計測、外部パッケージとの接続がある。テストフレームワークの記法を変えると、こうした周辺部分も確認対象になる。
特に注意したいのは、既存テストがPHPUnitのクラス構造に依存している場合だ。共通の基底テストクラス、独自のセットアップ、テスト用トレイト、データプロバイダー、モックヘルパーなどが複雑に積み重なっていると、単純な自動変換では意図を保てないことがある。
Laravelの機能テストでは、次のような要素が組み合わされる。
- 認証済みユーザーや権限の準備
- データベースの初期化とファクトリーの利用
- キュー、メール、通知、イベントの差し替え
- 例外処理を無効化した画面レスポンスの確認
- ルートモデルバインディングや認可処理の検証
- ファイルストレージや外部決済サービスのモック
- テスト後のデータ削除と並列実行への対応
これらはPestでも扱える。しかし、扱えることと、移行しただけで安全に変換できることは別だ。変換後にテストが通っていても、元のテストが意図していた境界条件まで維持されているとは限らない。テストが通ることは最低条件であり、テストの意味が変わっていないことをレビューで確認する必要がある。
PHPUnitからPestへの書き換えは、一括ではなく境界を決めて行う
既存資産を持つチームにとって、最大の論点は移行コストだ。
テストファイルが少なく、構造も単純なら、手作業で書き換えても大きな問題にはなりにくい。逆に、長年運用しているアプリケーションでは、テストの数だけでなく、テスト同士の前提や共通処理まで確認しなければならない。ファイル数だけを見て工数を見積もると、後から例外が増えて計画が崩れる。
Pestには、PHPUnit形式のテストを移行するための補助機能やプラグインがある。これらを使えば、一定のパターンを自動変換できる可能性がある。ただし、自動変換は完成品を作る機能ではなく、人間が確認すべき下書きを作る機能と考えた方がよい。
自動化しやすいのは、構造が単純なテストだ。テストメソッドの変換、クラス宣言の整理、基本的なアサーションの置換などは、機械的に処理しやすい。一方で、次のような部分は個別確認が必要になる。
共通セットアップの扱い
PHPUnitでは、クラス単位のセットアップにテストの前提条件をまとめることが多い。Pestにも共通処理をまとめる仕組みはあるが、適用範囲の考え方が変わる。ファイル単位なのか、ディレクトリ単位なのか、特定のテストだけなのかを整理しないと、以前は適用されていた準備処理が外れたり、逆に広すぎる範囲へ適用されたりする。
データプロバイダーの読み替え
入力値の組み合わせを使うテストでは、データプロバイダーの書き方が移行時の確認点になる。単純な配列であれば対応しやすいが、プロバイダー内部でデータベースやサービスコンテナにアクセスしている場合は注意が必要だ。テスト本体が実行される前に評価される処理があると、Laravelのアプリケーション状態との関係が変わることがある。
モックと依存性注入
モックの生成方法をPestの記法に変えても、モック対象の設計が改善するわけではない。モックが多すぎるテストは、記法を移行しても壊れやすいままだ。移行を機会に、外部サービスとの境界や依存性注入の構造を見直すことはできるが、それはテストフレームワークの移行とは別の作業として工数を扱うべきだ。
テストのグループと実行条件
CIで特定のグループだけを実行している場合、グループ指定やフィルターの挙動を確認する必要がある。ローカルでは全件通るのに、CIの一部ジョブだけテストが抜けるという事故は、移行時に起きやすい。テストが実行されていること自体を、実行ログやテスト件数で確認したい。
段階移行をするなら、機能領域で区切る方法が分かりやすい。たとえば、まず一部の機能テストを移行し、次に残りの機能テスト、最後にユニットテストやスモークテストへ進む。あるいは、変更頻度の高いディレクトリだけを対象にして、頻繁にレビューされるコードの読みやすさから改善していく方法もある。
ここで大切なのは、移行対象の境界を曖昧にしないことだ。「できるところから移行する」という方針は柔軟に見えるが、完了条件がなければ、PHPUnitとPestの混在が管理不能になる。混在自体は問題ではない。どの範囲をどの記法で書くのか、セットアップをどこに置くのか、レビューで何を基準にするのかが決まっていればよい。
共存は妥協ではなく、移行戦略になる
PestとPHPUnitの互換性を考えると、既存テストを残したまま新規テストをPestで書く方法は十分に現実的だ。Pestのテストランナーから、既存のPHPUnit形式のテストを実行できる構成を取れるため、すべてを一度に書き換える必要はない。
この共存戦略が有効なのは、プロジェクトの変更速度を落とさずに、Pestの効果を確認できるからだ。新機能のテストだけPestで書き、レビューで読みやすさを比較する。既存の複雑なテストはPHPUnitのまま維持し、変更が入ったタイミングで必要に応じて書き換える。これなら、移行のためだけにテストコードを大量変更する必要がない。
ただし、共存にはルールがいる。特に決めておきたいのは、次のような点だ。
- 新規ファイルはどちらの記法で作成するか
- 既存ファイルを変更するとき、記法を変えるか
- 共通セットアップをどの仕組みで管理するか
- アサーションの書き方を統一するか、併用するか
- ディレクトリやテストグループの命名をどうそろえるか
- 新メンバー向けのテスト作成手順をどこに記録するか
この方針がないと、同じプロジェクト内で似たテストが異なる設計になり、Pestを導入したメリットが薄れる。逆に、共存のルールが明確なら、PHPUnitの安定した既存資産を保ちながら、Pestの読みやすさを新しい領域へ取り込める。
「完全移行できないなら導入しない」という考え方は、既存プロジェクトでは強すぎる。段階移行の目的は、全ファイルの形式をそろえることではなく、変更コストに見合う範囲で開発体験を改善することだ。
Laravel専用プラグインは、移行理由になり得るが万能ではない
PestのLaravel向けプラグインを導入すると、Laravelのテスト機能をPestの書式の中で扱いやすくなる。HTTPリクエスト、認証、Artisanコマンド、例外処理、データベース、メールや通知など、Laravelで頻繁に使うテストヘルパーを、Pestのテストと組み合わせられる。
ここでの利点は、Laravelのテスト機能が増えることではない。PHPUnitでLaravelをテストしてきたチームなら、すでに同じ機能を利用できている。変わるのは、それらをテストの流れにどう配置できるかだ。
機能テストでは、次のような流れが頻繁に登場する。
1. テスト用ユーザーや権限を準備する。
2. 認証状態を作る。
3. Laravelのテストクライアントでリクエストを送る。
4. ステータスやレスポンスの内容を確認する。
5. データベース、メール、通知などの副作用を検証する。
Pestでは、この一連の流れをクラスのメソッドへ分散させず、テストケースの近くに置きやすい。テストの前提と検証対象の距離が近くなるため、読み手がコンテキストを行き来する回数を減らせる。
一方、プラグインを入れたからといって、Laravelのテスト設計が自動的に良くなるわけではない。アプリケーションの起動が重い、各テストで大量のデータを作る、外部サービスの差し替えが不十分といった問題は、Pestでもそのまま残る。
また、Laravelのバージョン、Pestのバージョン、PHPUnitのバージョン、プラグインの対応関係は確認が必要だ。新しいプロジェクトでは依存関係がまとまりやすいが、既存プロジェクトでは他のパッケージが古いPHPUnitの挙動に依存している可能性もある。移行前に、開発環境だけでなくCIのPHPバージョンや依存関係の解決結果まで確認したい。
並列実行はPestの専売特許ではない
Pestを選ぶ理由として、並列実行が挙げられることがある。テストを複数のプロセスへ分散して実行できれば、CIの待ち時間を短縮できる可能性がある。テストが増え、変更のたびに全体の実行を待つようなプロジェクトでは、並列化の価値は大きい。
ただし、並列実行はPestだけの機能ではない。PHPUnitでも、追加のツールやCI側の分割によって並列実行を構成できる。したがって、「並列実行をしたいからPestへ移行する」という判断は、現在のPHPUnit構成で何が不足しているのかを確認してから行うべきだ。
並列化で難しいのは、プロセスを分けて起動することではない。テスト同士が独立していることを保証することだ。
たとえば、次のような状態があると、並列実行で失敗しやすい。
- 固定されたメールアドレスや識別子を複数のテストが共有している
- 特定のテストが作ったデータを、別のテストが暗黙に利用している
- 一時ファイルやストレージの保存先が共通している
- 外部サービスのモック設定がプロセス間で分離されていない
- テストが実行順序に依存している
- データベースのリセットが十分でない
この問題は、PestかPHPUnitかとは別のテスト設計の問題だ。並列化を目的にするなら、まずテストを単独で実行しても同じ結果になるか、実行順序を変えても壊れないかを確認する必要がある。
並列実行を導入するときは、CIの全体時間だけでなく、失敗時の調査コストも見るべきだ。プロセスが分散されると、ログが追いにくくなることがある。実行時間が短くなっても、失敗原因の特定に時間がかかれば、チーム全体では得をしていない場合がある。
移行の判断ラインをどこに置くか
PestとPHPUnitの選択基準は、プロジェクトの規模だけでは決まらない。テストの量、チームの経験、変更頻度、CIの課題、将来のメンテナンス方針を合わせて見る必要がある。
新規のLaravelプロジェクト
新規プロジェクトでは、Pestを選ぶハードルは低い。過去のPHPUnit資産を引き継ぐ必要がなく、チーム内で記法を統一しやすいからだ。Laravel 11以降の標準的な流れに合わせたい場合や、テストを仕様書に近い形で管理したい場合にも、Pestは有力な選択肢になる。
ただし、チームにPHPUnitの経験者が多く、既存の社内テンプレートや共通ライブラリがPHPUnitを前提としているなら、無理にPestへ寄せる必要はない。新規プロジェクトであっても、組織全体の開発ルールとの一貫性は重要だ。
小規模から中規模の既存プロジェクト
テストの数が限られ、共通セットアップも単純なら、Pestへの移行を試しやすい。特に、これから追加するテストの量が多く、既存テストの変更頻度も高いプロジェクトでは、Pestの記法を早めに採用する意味がある。
一方で、既存テストが安定しており、チームが現状の書き方に困っていないなら、移行を急ぐ理由は弱い。Laravelのバージョンアップや新機能開発と同じタイミングで、変更対象のテストだけPestへ寄せる方が、費用対効果を説明しやすい。
大規模な既存リポジトリ
テストが大量にあり、複数チームが同じリポジトリを触っている場合、全体移行は慎重に考えたい。移行の技術的な難しさだけでなく、レビュー基準、ブランチの競合、ドキュメント、CIの安定性にも影響する。
この場合は、特定のディレクトリや新機能だけを対象にした試験導入が現実的だ。Pestのメリットが確認できなければ、PHPUnitを継続すればよい。確認できたとしても、すべてを移行せず、変更頻度の高い領域だけに適用する判断があってよい。
チームの習慣がPHPUnitに強く結び付いている場合
PHPUnitのクラス構造を前提に、テスト設計やレビューが長年整えられているチームでは、Pestの構文が必ずしも歓迎されるとは限らない。新しい記法を覚えるコストに対して、得られる改善が小さい可能性もある。
特に、メンバーがPestの構文に慣れるまで、テストの書き間違いやレビューの確認項目が増えることがある。新しい道具を導入するなら、学習期間を見込んだうえで、移行後の運用ルールまで用意したい。
移行前に見るべきなのは、テストの数より変更の痛み
テストファイルが五十以下ならPest、何百もあるならPHPUnitというように、件数だけで線を引くのは分かりやすい。しかし、それだけでは判断を誤る。
同じ百ファイルでも、単純なユニットテストが中心なのか、複雑な機能テストが中心なのかで移行コストは大きく違う。ファイル数が少なくても、共通基底クラスや独自ヘルパーが複雑なら、変換には時間がかかる。逆に、ファイル数が多くても、標準的な構造で分離されていれば段階移行しやすい。
見るべきなのは、次のような痛みだ。
- 新しいテストを書くとき、クラスやセットアップが負担になっているか
- テスト名だけでは仕様の意図を把握しづらいか
- レビューでテストの本体まで到達するのに時間がかかるか
- 既存テストの変更時に、共通処理の影響範囲が分かりにくいか
- CIの実行時間が開発サイクルの障害になっているか
- 新しいメンバーがテストコードを読むまでに時間がかかっているか
- テストが増えるほど、記法や構成の揺れが目立っているか
この痛みがないなら、Pestへの移行は技術的な好みの問題になりやすい。好みで移行しても構わないが、開発工数を使う以上、チーム内で目的を共有しておく必要がある。
反対に、テストが読まれない、書かれない、保守されないという問題があるなら、Pestの導入を改善のきっかけにできる。ただし、その場合も原因を構文だけに限定しないことだ。テストデータの準備が難しい、実行が遅い、失敗メッセージが分かりにくい、環境構築が複雑といった問題は、Pest以外の改善も必要になる。
僕が取るなら、Pestを新規領域に使い、既存資産は急いで壊さない
自分のプロダクトで判断するなら、まず新規プロジェクトではPestを選ぶ。既存資産がない段階なら、記法を統一しやすく、Laravelの標準的な流れにも合わせやすいからだ。
既存プロジェクトでは、最初から全件移行を目標にしない。新機能のテストをPestで書き、レビューのしやすさ、テストの命名、セットアップの扱いやすさをチームで確認する。そのうえで、変更頻度の高い既存テストを候補にする。
移行を検証するときは、次の三つを分けて考える。
書き換えやすさ
自動変換や手作業によって、どこまで安全にPest形式へ変えられるかを見る。変換できたファイルの割合だけではなく、レビューに必要な時間や、変換後に設計を見直す必要がある箇所も記録する。
読みやすさ
テストが短くなったかではなく、初めて読む人が仕様を理解しやすくなったかを見る。レビューで説明が減ったか、テスト名から対象の振る舞いが分かるか、失敗時に原因を追いやすいかが判断材料になる。
実行と保守
CIの実行方法、カバレッジ、テストフィルター、並列実行、開発環境のセットアップが以前と同じように機能するかを確認する。Pestを導入したことで、別の運用コストが増えていないかも見ておく。
この三つを確認したうえで効果があるなら、Pestの適用範囲を広げればよい。効果が小さいなら、PHPUnitを使い続ければよい。どちらを選んでも、Laravelのテストを適切に保守できているなら、プロダクトの品質が自動的に劣るわけではない。
まとめ:移行すべきかではなく、どの痛みを解消するか
LaravelにおけるPestとPHPUnitの違いは、単純な新旧比較ではない。PestはPHPUnitを土台にして、クロージャー構文、expect()、テストの整理、Laravelとの統合を通じて、テストを書くときと読むときの摩擦を減らす選択肢だ。
Pestを選びやすいのは、新規のLaravelプロジェクト、テストを仕様書として読ませたいチーム、既存テストの記法やレビューに明確な不満があるプロジェクトだ。逆に、PHPUnitが適しているのは、大規模な既存資産を安定運用したい場合、周辺ツールや社内標準との互換性を優先する場合、移行による利益がまだ見えていない場合になる。
既存テストをすべて書き換える必要はない。PestとPHPUnitは共存できる。新規テストをPestで書き、変更が入った部分から段階的に移行する方法もある。移行の境界とルールを決めておけば、共存は中途半端な妥協ではなく、リスクを抑えた運用戦略になる。
Laravel 11の標準構成は、Pestを試すきっかけにはなる。しかし、それだけで既存プロジェクトの全テストを移行する理由にはならない。確認すべきなのは、Pestが流行しているかどうかではなく、自分たちのテストが抱えている痛みを本当に減らせるかどうかだ。
プロダクトグロースは、新しい道具を採用した数を競う活動ではない。チームがコードを読み、テストを書き、壊れた仕様を早く見つけられる状態をつくる活動だ。PestもPHPUnitも、そのための道具に過ぎない。今のプロジェクトで一番重い痛みを見極め、その痛みに効く範囲で選ぶ。それが、Laravel開発でテスト移行を進めるときの判断ラインになる。
Related reading: Laravel・PHP開発を選ぶときの確認ポイント and Laravel SanctumとPassport:API認証における選定基準の最適解.