🌳
【Laravel8・PHP8】PHPUnitのテストケースの中でseedingするとPDOException
背景
テストでマスターデータが必要になった。
テストケース中でシーディングするときに詰まったのでメモ。
環境
- Laravel 8.40
- PHP 8.0
- MySQL 8.0
事象
テストケースの中でシーディング実行するとエラー発生
$ php artisan test ./tests/Unit/UserTest.php
FAIL Tests\Feature\UserTest
⨯ seed
---
• Tests\Feature\UserTest > seed
PDOException
There is no active transaction
at vendor/laravel/framework/src/Illuminate/Database/Concerns/ManagesTransactions.php:279
275▕ */
276▕ protected function performRollBack($toLevel)
277▕ {
278▕ if ($toLevel == 0) {
➜ 279▕ $this->getPdo()->rollBack();
280▕ } elseif ($this->queryGrammar->supportsSavepoints()) {
281▕ $this->getPdo()->exec(
282▕ $this->queryGrammar->compileSavepointRollBack('trans'.($toLevel + 1))
283▕ );
Tests: 1 failed
Time: 0.39s
実際のコード
UsersTableSeeder
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
class UsersTableSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
DB::table('users')->truncate();
DB::table('users')->insert([
'id' => 1,
'name' => 'test name 1',
'email' => 'test1@test.com',
'email_verified_at' => now(),
'password' => \Hash::make('password'),
'remember_token' => Str::random(10),
]);
}
}
UserTest.php
<?php
namespace Tests\Unit;
use Database\Seeders\UsersTableSeeder;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class UserTest extends TestCase
{
use RefreshDatabase;
public function test_seed()
{
$this->seed(UsersTableSeeder::class);
$this->assertDatabaseHas('users', [
'id' => 1,
'name' => 'test name 1',
'email' => 'test1@test.com',
]);
}
}
解決策
テストケース中で使っているTraitをRefreshDatabase
から DatabaseMigrations
に変える。
コード
UserTest.php
<?php
namespace Tests\Unit;
use Database\Seeders\UsersTableSeeder;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Tests\TestCase;
class UserTest extends TestCase
{
use DatabaseMigrations;
public function test_seed()
{
$this->seed(UsersTableSeeder::class);
$this->assertDatabaseHas('users', [
'id' => 1,
'name' => 'test name 1',
'email' => 'test1@test.com',
]);
}
}
結果
$ php artisan test ./tests/Unit/UserTest.php
PASS Tests\Feature\UserTest
✓ seed
Tests: 1 passed
Time: 0.42s
調査 (WIP
検索してみるとLaravelやPHPのバージョン起因によるもの?
同様なエラーについて議論しているIssueを発見
コメントにはこの一文が
Before PHP 8, while there's no active transaction PDO doesn't throw any error but this not the same in PHP 8.
つまり8系以前でも同様の事象は発生していたが、エラーにならなかっただけと考えられる。
その事象がなぜ起きるかについては調査中
※MySQLのドキュメントにはTruncateは暗黙的にコミットされるようなのでこれが関係している?
※Laravelのissueで修正については議論されている模様
補足
間違い等のご指摘歓迎いたします。
Discussion