laravelのテスト、始め方、完全版

この記事は、Laravel Framework 10.7.1で動作確認しています。

今やってるlaravelの開発もほぼ目処がついたところでそろそろテストでも書くかなと思って、調べてみたら、案の定ろくな情報がない。
まあ、テスト大事だよね、と言いつつ、誰もテストなんて書いていないという実態、というところではないですかね。
ちなみに真っ先にchatGpt様にお伺いしてみたが、ものの見事に動かないソースを紹介されました。


・・・ということで始めよう。テストね、うかつにchatGpt様のソースなんぞを実行すると、DBを空っぽにされてしまうので注意しよう（爆。
つまり、テストはDBを使うのでテスト用のDBを用意する必要があるのだ。

実に面倒である。
当初はもうホントにミニマムな開発でマイグレーションとか必要ないかなと思っていたが、こうなるとホイホイDBを作れた方が楽である。マイグレーションを設定しよう。まずはコマンドラインから、

```php
php artisan make:migration create_customers_table --table=customers
```

make:migrationする。そうすると、database/migrationsの下に`2023_04_17_033534_create_customers_table.php`　というようなファイルが作られる。これを見本として修正していけばよい。

```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
php artisan migrate
```

とすればよい。
一気にテーブルが生成される。

あと、テーブルに初期値を入れることもあるだろう。最初の管理者ユーザーとか、消費税率とか、そういうのは `/database/seeders/DatabaseSeeder.php` に書いておく。
以下は消費税率の例。

```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
php artisan db:seed
```

テスト環境についても同様で、テスト用のDBを作ったら、.envファイルをコピーして `.env.testing` を作る。

```.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
php artisan key:generate --env=testing
```

次に直下の `phpunit.xml` を編集する。

```phpunit.xml
        <env name="DB_CONNECTION" value="mysql"/>
        <env name="DB_DATABASE" value="test_my_db"/>
```

おそらくこの2行、コメントアウトされているので、コメントを外して、それぞれvalueを書き換える。
テスト用DBにマイグレイトしておく。

```php
php artisan migrate --env=testing
```

最後にコマンドで、

```php
php artisan config:clear
```

これで、キャッシュされた設定ファイルが削除され、アプリケーションが再起動される。やっておかないと、実際のDBの方にバンバン書き込まれる。

ついでなのでここでFactoryも作っておこう。
これはテスト時にダミーデータを生成するために使用される。

```php
<?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
php artisan make:test CustomerControllerTest
```

こんなソースが作られるので、

```php
<?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);
    }
}
```
自分の考えるテストを追加していく。

```php
public function testGetIndex(): void
    {
        $response = $this->get('/customer');

        $response->assertSee('顧客一覧');
        $response->assertOk();
        $response->assertStatus(200);
        $response->assertViewIs('customers.index');
    }
```
    
    
出来たら以下のコマンドでテストを実行する。

```php
php artisan test .\tests\Feature\CustomerControllerTest.php
```

実際のところ、これは管理者画面なので、ログイン認証済みのセッションを持っていないとログイン画面にリダイレクトで戻されてしまう。
そうするとassertStatus(200)のはずが302を返すので一生合格しない。

そういう場合はresponse getのところにsession を挟む。

```php
$response = $this->withSession(['account' => '管理者'])->get(route('customer'));
```
あとデータベースへのインサートのテストは、

```php
public function testInsertSuccess()
    {
        $customer = new Customer();
        $customer->customer_number = "1012";
        $customer->name = "株式会社バーベキュー";
        $result = $customer->save();
        $this->assertTrue($result);
    }
```
こんな感じでオッケーだ。

新規登録メソッドのテストはこんな感じで

```php
public function testStore()
    {
        $customerData = [
            'customer_number' => '1026',
            'name' => 'テスト太郎',
        ];

        $response = $this->post('/customer/store', $customerData);
        $response->assertRedirect(route('customer'));

        $this->assertDatabaseHas('customers', $customerData);
    }
```    
    
編集の更新処理は、こんな感じで。ここでFactoryが登場する。

```php
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
<?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系の設定ファイルを整備して、テストを書きつつ、コントローラを埋めていく、というのが理想的だろう。
    
テーブルが多いと気が遠くなるような作業だろうが。