この記事は、Laravel Framework 10.7.1で動作確認しています。
今やってるlaravelの開発もほぼ目処がついたところでそろそろテストでも書くかなと思って、調べてみたら、案の定ろくな情報がない。
まあ、テスト大事だよね、と言いつつ、誰もテストなんて書いていないという実態、というところではないですかね。
ちなみに真っ先にchatGpt様にお伺いしてみたが、ものの見事に動かないソースを紹介されました。
・・・ということで始めよう。テストね、うかつにchatGpt様のソースなんぞを実行すると、DBを空っぽにされてしまうので注意しよう(爆。
つまり、テストはDBを使うのでテスト用のDBを用意する必要があるのだ。
実に面倒である。
当初はもうホントにミニマムな開発でマイグレーションとか必要ないかなと思っていたが、こうなるとホイホイDBを作れた方が楽である。マイグレーションを設定しよう。まずはコマンドラインから、
php artisan make:migration create_customers_table --table=customers
make:migrationする。そうすると、database/migrationsの下に2023_04_17_033534_create_customers_table.php
というようなファイルが作られる。これを見本として修正していけばよい。
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration {
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('customers', function (Blueprint $table) {
$table->id();
$table->integer('customer_number')->comment('お客様No');
$table->string('name')->comment('会社名');
$table->string('yomi')->nullable()->comment('よみがな');
$table->integer('status')->default(0)->comment('状態');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::drop('customers');
}
};
こんな感じだ。
up() にクリエイトテーブル、down() にドロップテーブル。
id() とtimestamps() は自動で設定してくれる。
(idは主キーでオートインクリメント、timestampsは登録日時と更新日時)
他のカラムは、何も指定しないと全部 not null が指定されてしまうので、->nullable() をくっつける。
こういうのをテーブルの数だけ作る。
作り終わったら、コマンドから、
php artisan migrate
とすればよい。
一気にテーブルが生成される。
あと、テーブルに初期値を入れることもあるだろう。最初の管理者ユーザーとか、消費税率とか、そういうのは /database/seeders/DatabaseSeeder.php
に書いておく。
以下は消費税率の例。
public function run(): void
{
DB::table('tax_rates')->insert([
'start_date' => '2019-10-01',
'end_date' => '2099-12-31',
'tax_rate' => 0.1,
]);
}
出来たら以下のコマンドでインサートされる。
php artisan db:seed
テスト環境についても同様で、テスト用のDBを作ったら、.envファイルをコピーして .env.testing
を作る。
DB_CONNECTION=mysql
DB_HOST=loalhost
DB_PORT=3306
DB_DATABASE=test_my_db
DB_USERNAME=my_user
DB_PASSWORD=password
DB_DATABASE のところをテスト用DBの名前に変更する。
あとAPP_KEY というのがあるので、以下のコマンドを打って更新しておく。
php artisan key:generate --env=testing
次に直下の phpunit.xml
を編集する。
<env name="DB_CONNECTION" value="mysql"/>
<env name="DB_DATABASE" value="test_my_db"/>
おそらくこの2行、コメントアウトされているので、コメントを外して、それぞれvalueを書き換える。
テスト用DBにマイグレイトしておく。
php artisan migrate --env=testing
最後にコマンドで、
php artisan config:clear
これで、キャッシュされた設定ファイルが削除され、アプリケーションが再起動される。やっておかないと、実際のDBの方にバンバン書き込まれる。
ついでなのでここでFactoryも作っておこう。
これはテスト時にダミーデータを生成するために使用される。
<?php
namespace Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Customer>
*/
class CustomerFactory extends Factory
{
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
return [
//
'customer_number' => fake()->randomDigit(),
'name' => fake()->name(),
];
}
}
で、後はテストを書けばよい。まずはコマンドでひな型を作って、
php artisan make:test CustomerControllerTest
こんなソースが作られるので、
<?php
namespace Tests\Feature;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Tests\TestCase;
class CustomerControllerTest extends TestCase
{
/**
* A basic feature test example.
*/
public function test_example(): void
{
$response = $this->get('/');
$response->assertStatus(200);
}
}
自分の考えるテストを追加していく。
public function testGetIndex(): void
{
$response = $this->get('/customer');
$response->assertSee('顧客一覧');
$response->assertOk();
$response->assertStatus(200);
$response->assertViewIs('customers.index');
}
出来たら以下のコマンドでテストを実行する。
php artisan test .\tests\Feature\CustomerControllerTest.php
実際のところ、これは管理者画面なので、ログイン認証済みのセッションを持っていないとログイン画面にリダイレクトで戻されてしまう。
そうするとassertStatus(200)のはずが302を返すので一生合格しない。
そういう場合はresponse getのところにsession を挟む。
$response = $this->withSession(['account' => '管理者'])->get(route('customer'));
あとデータベースへのインサートのテストは、
public function testInsertSuccess()
{
$customer = new Customer();
$customer->customer_number = "1012";
$customer->name = "株式会社バーベキュー";
$result = $customer->save();
$this->assertTrue($result);
}
こんな感じでオッケーだ。
新規登録メソッドのテストはこんな感じで
public function testStore()
{
$customerData = [
'customer_number' => '1026',
'name' => 'テスト太郎',
];
$response = $this->post('/customer/store', $customerData);
$response->assertRedirect(route('customer'));
$this->assertDatabaseHas('customers', $customerData);
}
編集の更新処理は、こんな感じで。ここでFactoryが登場する。
public function testUpdate()
{
$customer = Customer::factory()->create();
$updatedData = [
'id' => $customer->id,
'customer_number' => '2000';,
'name' => 'テスト次郎',
];
$response = $this->post('/customer/update/', $updatedData);
$response->assertRedirect(route('customer'));
$this->assertDatabaseHas('customers', $updatedData);
}
テストが終ったらDBをクリアしてくれる設定にしたい場合は、以下のようにRefreshDatabaseをuseしておけばよい。
<?php
namespace Tests\Feature;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
use App\Models\Customer;
class CustomerControllerTest extends TestCase
{
use RefreshDatabase;
以上、最初から計画的に、migration、 seed、 factoryといったDB系の設定ファイルを整備して、テストを書きつつ、コントローラを埋めていく、というのが理想的だろう。
テーブルが多いと気が遠くなるような作業だろうが。