概要
Laravel5.2の時にやってたテストデータの登録方法が上手くいかず、少しハマったのでそのまとめ。
環境
Laravel 5.5.9
PHP 7.1.3
MySQL 5.7.19
Laravel5.2の時の方法
確かこんな感じで、@beforeアノテーションでテスト前にSeederを実行してました。
<?php
namespace Tests\Unit;
use Illuminate\Database\Seeder;
use Tests\TestCase;
use Illuminate\Foundation\Testing\DatabaseMigrations;
class ExampleTest extends TestCase
{
use DatabaseMigrations;
/**
* @before
*/
public function setUpFixtures()
{
$this->seed(__CLASS__ . 'Seeder');
}
/**
* @test
*/
public function サンプルテスト()
{
$this->markTestIncomplete('テストを実装する!');
}
}
/**
* テストデータ登録Seeder
*/
class ExampleTestSeeder extends Seeder
{
public function run()
{
// @todo テストデータ登録
}
}
Laravel5.5でも同様にやってみる
RefreshDatabaseトレイトが追加されてるので、ついでに変更。
<?php
namespace Tests\Unit;
use Illuminate\Database\Seeder;
use Tests\TestCase;
use Illuminate\Foundation\Testing\RefreshDatabase;
class ExampleTest extends TestCase
{
use RefreshDatabase;
/**
* @before
*/
public function setUpFixtures()
{
$this->seed(__CLASS__ . 'Seeder');
}
/**
* @test
*/
public function サンプルテスト()
{
$this->markTestIncomplete('テストを実装する!');
}
}
/**
* テストデータ登録Seeder
*/
class ExampleTestSeeder extends Seeder
{
public function run()
{
// @todo テストデータ登録
}
}
結果
エラーが発生。
どうもアプリケーションクラスの初期化が終わってないっぽい。
アプリケーションの初期化の方法が変わったのかな?(後で調べて追記するかも)
1) Tests\Unit\ExampleTest::サンプルテスト
Error: Call to a member function call() on null
(追記:2017-09-22)
PHPUnit側の処理が変わったみたい。(PHPUnit\Util\Test::getHookMethodsメソッド)
(PHPUnitのバージョンによって挙動が変わるなら、サンプルソースも問題なので修正しました)
- Laravel5.2で使っていた時のバージョン:4.8.31
- @beforeClass・@before・@after・@afterClass 全て、見つかった順で変数へ格納
- Laravel5.5で使っているバージョン:6.3.0
- @beforeClass・@beforeはarray_unshiftで変数へ格納、@after・@afterClass は見つかった順で変数へ格納
原因
ソースをみた感じだと、setUpメソッド内でアプリケーションの初期化を行なっているが、PHPUnit的にはsetUpより@beforeアノテーションで定義したものが先に呼ばれる模様。
(array_shift array_unshiftで、見つかった@beforeのメソッドを格納してるので、後に見つかったものが先に呼ばれるっぽい。そしてデフォルト値にsetUpが入っている。)
Laravel5.5で動くよう修正
afterApplicationCreatedメソッドでアプリケーションの初期化完了時のコールバックを定義できるみたい。
<?php
namespace Tests\Unit;
use Illuminate\Database\Seeder;
use Tests\TestCase;
use Illuminate\Foundation\Testing\RefreshDatabase;
class ExampleTest extends TestCase
{
use RefreshDatabase;
/**
* @before
*/
public function setUpFixtures()
{
// 下のように書き換え
//$this->seed(__CLASS__ . 'Seeder');
// 訂正(2017-09-22)
//$this->afterApplicationCreated(function() {
// $this->seed(__CLASS__ . 'Seeder');
//});
if ($this->setUpHasRun) {
// アプリケーションのセットアップが完了しているならそのまま実行
$this->seed(__CLASS__ . 'Seeder');
} else {
// アプリケーションのセットアップが完了していないならコールバックで設定
$this->afterApplicationCreated(function() {
$this->seed(__CLASS__ . 'Seeder');
});
}
}
/**
* @test
*/
public function サンプルテスト()
{
$this->markTestIncomplete('テストを実装する!');
}
}
/**
* テストデータ登録Seeder
*/
class ExampleTestSeeder extends Seeder
{
public function run()
{
// @todo テストデータ登録
}
}
再度実行して動くことを確認。
$ vendor/bin/phpunit
PHPUnit 6.3.0 by Sebastian Bergmann and contributors.
I 1 / 1 (100%)
Time: 580 ms, Memory: 12.00MB
OK, but incomplete, skipped, or risky tests!
Tests: 1, Assertions: 0, Incomplete: 1.
まとめ
setUp() > @before > テスト実行 の順だと思い込んでた...
(2017-09-22)
追記しましたが、PHPUnitのバージョンで挙動が変わるっぽい。
ちゃんと確認してよかった。。サンプルソースも修正しました。
内容に間違いがあるとか、もっと良い方法があるとか、何かあればご指摘ください。
以上です。