- Kahlanについてはこちらの記事を参照してください
目次
環境
- Laravel 11 / PHP 8.3
- Kahlan 5.2.6
- Mac OS(Sonoma 14.1)
準備
テストコードの用意
- 今回はControllerで試しました
- ユーザーIDを受け取ってユーザー情報をJSONで返すようなコードです
- User Modelクラスは記載ありませんが、今回データベースを用意するのが手間なので
userId=1
時は「Yamada Taro」さん、それ以外の時はデータがないような挙動をするようにしています。
<?php
namespace App\Http\Controllers;
use App\Models\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\ValidationException;
/**
* Class SampleController
* @package App\Http\Controllers
*/
class SampleController extends Controller
{
/**
* ユーザー情報取得
* @param Request $request
* @return JsonResponse
* @throws ValidationException
*/
public function getUserInfo(Request $request): JsonResponse
{
// パラメータバリデーション
$validator = Validator::make($request->all(), [
'userId' => 'required|integer'
]);
// バリデーション失敗
if ($validator->fails()) {
return response()->json(['error' => 'Request format error'], 400);
}
// 指定ユーザー情報取得
$user = User::find($validator->validated()['userId']);
if (!$user) {
return response()->json(['error' => 'User not found'], 404);
}
return response()->json($user);
}
}
テストケース
- こちらのコードで今回テストするのは以下の3つです
- リクエストバリデーション
- ユーザーが存在しなかった
- ユーザーが存在した
specファイルを作成する
- 今回は
spec/app/Http/Controllers/SampleController.spec.php
に追加しました
<?php
use App\Http\Controllers\SampleController;
use Illuminate\Http\Request;
describe('SampleController Test', function ()
{
beforeAll(function () {
$this->app = require __DIR__ . '/../../../../bootstrap/app.php'; // Laravelアプリケーションのロード
$this->app->make(Illuminate\Contracts\Console\Kernel::class)->bootstrap();
$this->controller = new SampleController();
});
describe('getUserInfo', function () {
context('条件:ユーザーIDが不正', function () {
it('結果:バリデーションエラー', function () {
$request = Request::createFromGlobals();
$request->merge(['userId' => 'ほげ']);
$response = $this->controller->getUserInfo($request);
expect($response->getStatusCode())->toBe(400);
expect($response->getData()->error)->toBe('Request format error');
});
});
context('条件:ユーザーIDが指定された時', function () {
it('結果:ユーザーが存在しなかった', function () {
$request = Request::createFromGlobals();
$request->merge(['userId' => 2]);
$response = $this->controller->getUserInfo($request);
expect($response->getStatusCode())->toBe(404);
expect($response->getData()->error)->toBe('User not found');
});
it('結果:ユーザーが存在した', function () {
$request = Request::createFromGlobals();
$request->merge(['userId' => 1]);
$response = $this->controller->getUserInfo($request);
expect($response->getStatusCode())->toBe(200);
expect($response->getData()->name)->toBe('Yamada Taro');
});
});
});
});
テストの記述に関して
beforeEach, afterEach, beforeAll, afterAll
- beforeEach, afterEach
- 各テストケース実行の前後で処理したい処理がある場合に使用します。
- 良くあるのはテストケースごとにオブジェクトを新しく作成したり、データベースの状態をリセットする場合、フィクスチャをロードする場合などです。
- 但し作り方によってはbeforeAll, afterAllで良い場合があり、テスト実行時間にも関わるためどちらを利用するかはよく考える必要があります。
- beforeAll, afterAll
- 全てのテストが実行される前後で処理したい処理がある場合に使用します。
- 一度だけ初期化が必要な処理など。
describe, context, it
- describe
- テスト対象のメソッドやクラスの全体的なコンテキストを設定するために使用します。
- クラスやメソッド名の名前を含むのが一般的です。
- context
- 特定の状況や条件下でのテストをグループ化するために使用します。
- it
- 実際のテストコードを記述するために使用します。
- テストする具体的な条件や期待される結果を短く、明確に記述します。
テストを実行する
vendor/bin/kahlan --reporter=verbose
_ _
/\ /\__ _| |__ | | __ _ _ __
/ //_/ _` | '_ \| |/ _` | '_ \
/ __ \ (_| | | | | | (_| | | | |
\/ \/\__,_|_| |_|_|\__,_|_| |_|
The PHP Test Framework for Freedom, Truth and Justice.
src directory :
spec directory : /Users/hoge/factory/blog/kahlan/spec
SampleController Test
getUserInfo
条件:ユーザーIDが不正
✓ it 結果:バリデーションエラー
条件:ユーザーIDが指定された時
✓ it 結果:ユーザーが存在しなかった
✓ it 結果:ユーザーが存在した
Expectations : 6 Executed
Specifications : 0 Pending, 0 Excluded, 0 Skipped
Passed 3 of 3 PASS in 0.176 seconds (using 32MB)
まとめ
- 特別な設定など不要で簡単にテストを導入できた。
- describe, context, itの指定が分かりやすい。
- (テスト名を日本語で書きたい人なので有り難い)
- 実際のアプリケーションで利用するためにはkahlan用のbootstrap書いたり、ユーティリティクラスを追加した方が良い。