こんにちは!
今回はLaravelでの「テストコードの書き方」というテーマで解説していきます。
Webアプリケーション開発において、コードの品質を向上させて安心して開発を進めるためには、適切なテストが不可欠です。Laravelのテストコードの書き方を学んで、堅牢なアプリケーションを構築していきましょう!
本記事では、以下のようなことがわかります。
- テストコードを書くための設定方法
- テストコードの書き方
- テストデータを用意する方法
「Laravelでテストコードを書く方法を知りたい」
とお考えの方は参考になるかと思います。
それでは、さっそくみていきましょう!
テストコードを書くためのセットアップ
まずはLaravelをインストールします。
$ composer create-project laravel/laravel --prefer-dist test
インストールが完了したら、テスト用の.envファイルを作成していきます。
$ cp .env.example .env.testing
.env.testingを開いて、DBの環境変数を設定していきます。
DB_CONNECTION=testing
DB_HOST=ホスト名
DB_PORT=ポート番号
DB_DATABASE=データベース名
DB_USERNAME=root
DB_PASSWORD=root
DB_SOCKET=/Applications/MAMP/tmp/mysql/mysql.sock
データベース名やポート番号などは環境に合わせて適時設定してください。筆者はMAMP環境で開発しているため、「DB_SOCKET」も追記しています。
次に、テスト用のDBを作成します。
MySQLにログインして以下のコマンドを実行します。
mysql> CREATE DATABASE test_db;
DBが作成されたことを確認しておきます。
mysql> SHOW DATABASES;
「config/database.php」ファイルを開いて、LaravelのDB設定を行います。
connections
配列の中に以下を追記します。
'testing' => [
'driver' => 'mysql',
'url' => env('DATABASE_URL'),
'host' => env('DB_HOST', '127.0.0.1'),
'port' => env('DB_PORT', '3306'),
'database' => env('DB_DATABASE', 'forge'),
'username' => env('DB_USERNAME', 'forge'),
'password' => env('DB_PASSWORD', ''),
'unix_socket' => env('DB_SOCKET', ''),
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'prefix' => '',
'prefix_indexes' => true,
'strict' => true,
'engine' => null,
'options' => extension_loaded('pdo_mysql') ? array_filter([
PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
]) : [],
],
connections
配列はDBの接続設定を行っています。
次に、phpunitの設定を行います。
「phpunit.xml」ファイルを開いて、以下の部分を編集します。
<env name="DB_CONNECTION" value="testing"/>
<env name="DB_DATABASE" value="test_db"/>
これでひとまず初期セットアップ完了です!
テストデータの準備
テストデータは基本的にSeederとFactoryを用いて作成していきます。
今回はユーザーデータと投稿データを作成していこうと思います。そのために、まずはマイグレーションファイルを作成してposts
テーブルを用意していきます。
$ php artisan make:migration create_posts_table
マイグレーションファイルには以下のように記述しておきます。
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('user_id')->comment('ユーザID');
$table->string('title')->comment('タイトル');
$table->text('body')->comment('投稿本文')->nullable();
$table->text('image_path')->comment('画像パス')->nullable();
$table->timestamps();
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('posts');
}
次に、Factoryを作成していきましょう。ユーザーのFactoryクラスはデフォルトで用意されているため、投稿データのみです。
$ php artisan make:factory PostFactory
definition
メソッドに以下のように書いておきます。
public function definition(): array
{
$user_id = User::where('email', 'admin@example.com')->first()->id;
return [
'user_id' => $user_id,
'title' => fake()->word(),
'body' => fake()->text()
];
}
ユーザーデータは「admin@example.com」というメールアドレスで作成する予定なので、このテストユーザーを指定してuser_idに入れるようにしています。
続いてSeederを用意していきます。
ユーザーデータは一人分しか用意するつもりはないので、UserSeederは以下のようになります。
use App\Models\User;
use Illuminate\Support\Facades\Hash;
public function run(): void
{
$user = new User();
$user->name = 'admin';
$user->email = 'admin@example.com';
$user->password = Hash::make('adminpass');
$user->save();
}
一人分であれば、直接SQLを実行しても良いかなとは思います。
投稿用のSeederを作成するため、以下のコマンドを実行します。
$ php artisan make:seeder PostSeeder
投稿用のテストデータは10件分用意します。
public function run(): void
{
\App\Models\Post::factory(10)->create();
}
最後に、「DatabaseSeeder」に登録しておきます。
public function run(): void
{
$this->call(UserSeeder::class);
$this->call(PostSeeder::class);
}
このDatabaseSeederに各Seederクラスを登録しておかないと、Seederが走りません。初心者の方は引っ掛かりがちなので覚えておきましょう。
こちらのSeederは後ほどテストコードを書く際に必要になります。
これでテストデータの準備は完了です!
Laravelでテストデータを作る際は、このようにSeederとFactoryを活用しながら作成していくのが基本です。
テストコードの書き方
お待たせしました。本題のテストコードの書き方について解説していきます。
Laravelのテストコードはtestsフォルダの中に格納されます。
testsフォルダの中に「Feature/」と「Unit/」がありますが、それぞれ機能テストと単体テストのディレクトリになります。
メソッド単位やクラス単位でテストしたい場合はUnitディレクトリの方にテストコードを書き、一つの機能全体をテストしたい場合はFeatureディレクトリでテストコードを書くことになります。
今回はFeatureディレクトリの方に書いていきます。
$ php artisan make:test Post/StoreTest
今回は仮で投稿機能のテストコードを書いてみたいと思います。
まずはテスト対象のコードを書いてみます。
「routes/web.php」を編集します。
Route::post('/post/store', [PostController::class, 'store'])->name('post.store');
次に、コントローラを作成します。
$ php artisan make:controller PostController
Postモデルも作成します。
$ php artisan make:model Post
それでは、PostControllerで投稿処理を書いていきます。
public function store(Request $request)
{
$request->validate([
'title' => ['required', 'max:255'],
'body' => ['required'],
]);
$post = new Post();
$post->title = $request->title;
$post->body = $request->body;
$post->user_id = $request->user()->id;
$post->save();
return redirect()->route('post.index');
}
こちらのテストコードを書いていきます。
それでは、テストファイルに戻って以下のように書きます。
<?php
namespace Tests\Feature\Post;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Illuminate\Support\Facades\Storage;
use Illuminate\Http\UploadedFile;
use Tests\TestCase;
use App\Models\User;
class StoreTest extends TestCase
{
use RefreshDatabase;
private $login_user;
public function setUp(): void
{
parent::setUp();
$this->seed();
$this->login_user = User::where('email', 'admin@example.com')->first();
$this->actingAs($this->login_user)->post(route('login'), [
'email' => 'admin@example.com',
'password' => 'adminpass',
]);
}
/**
* A basic feature test example.
*/
public function test_store(): void
{
// login_userが認証を通過しているとする
$this->assertAuthenticatedAs($this->login_user);
$response = $this->actingAs($this->login_user)->post(route('post.store'), [
'title' => 'Test Title',
'body' => 'Test Body',
]);
$response->assertStatus(302);
$response->assertRedirect(route('post.index')); // リダイレクト確認
$this->assertDatabaseHas('posts', [
'title' => 'Test Title',
'body' => 'Test Body',
]); // DBに登録されていることを確認
}
}
setUp()
メソッドはテストコード実行前に走ります。このメソッドの中でテストデータの準備などを行うことができます。また、いくつテストケースがあっても毎回走ってくれるので、共通で用意しておきたいテストデータなどがある場合は、このメソッド内で用意するのが基本です。
テストコードではassert系のメソッドで処理が正しく行われているか確認できます。
以下、本テストコードで使われているassert系メソッドです。
- assertStatus・・・レスポンスのステータスコードを確認する
- assertRedirect・・・指定したルートへのリダイレクトを確認する
- assertDatabaseHas・・・指定したテーブル内に指定したデータが登録されている確認する
これら以外にもたくさんあるので、ぜひ調べてみてください。
また、以下のRefreshDatabaseトレイトを使うことで一つのテストが終わると自動的にDBをクリーンアップしてくれます。
use RefreshDatabase;
これを行わないと、2つ目以降のテストを実行する際に前のテストのデータがDBに残ってしまい、他のテストに影響が出てしまいます。
動作確認
最後に、動作確認を行います。
テストコードを実行する前に、以下を必ず実行してください。
$ php artisan config:clear && php artisan cache:clear
これを行わないと、テスト用ではない方の本番のDBに変更が入ってしまう可能性があります。
それでは、テストコードを実行してみます。
$ php artisan test
以下のようになれば成功です。
逆に、失敗する場合は以下のようになります。
失敗した場合はエラーメッセージが出力されるので、エラーメッセージを確認し適時対処しましょう。
それでは、今回は以上です。
コメント