Laravel 8.0 测试中刷新数据库时“没有活动事务”

Era*_*els 9 php mysql phpunit laravel

使用 php 8.0.2 和 Laravel 8.37.0,我正在运行测试,其中每个测试都应该刷新数据库数据,因为每个测试都有冲突的数据(由于唯一的约束)。
将内存数据库与 SQLite 一起使用,这是可行的,但是当我切换到 MySQL (v8.0.23) 时,我收到下一个错误:

1) Tests\Feature\Controllers\AuthControllerTest::testSuccessLogin
PDOException: There is no active transaction
Run Code Online (Sandbox Code Playgroud)

并且此后的测试由于数据已插入且测试后未清除而失败。

我想做的测试是:

<?php

namespace Tests\Feature\Controllers;

use App\Models\User;
use App\Models\User\Company;
use App\Repositories\UserRepository;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

class AuthControllerTest extends TestCase
{
    use RefreshDatabase;

    protected array $connectionsToTransact = ['mysql'];

    public function testSuccessLogin(): void
    {
        $this->artisan('migrate-data');

        /** @var User $user */
        $user = User::factory()->create([
            'email' => 'test@website.com'
        ]);

        $this->app->bind(UserRepository::class, function() use ($user) {
            return new UserRepository($user, new Company());
        });

        $loginResponse = $this->post('/api/login', [
            'email' => 'test@website.com',
            'password' => 'password'
        ]);

        $loginResponse->assertStatus(200);
        $loginResponse->assertJsonStructure([
            'data' => [
                'user' => [
                    'name',
                    'surname',
                    'email',
                    'abilities'
                ],
                'token',
            ]
       ]);
    }
}
Run Code Online (Sandbox Code Playgroud)

执行此测试并检查数据库后,数据仍然存在。有和没有线protected array $connectionsToTransact = ['mysql'];给我相同的结果。

我的phpunit.xml文件看起来像:

<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:noNamespaceSchemaLocation="./vendor/phpunit/phpunit/phpunit.xsd"
     bootstrap="vendor/autoload.php"
     colors="true"
>
    <testsuites>
        <testsuite name="Unit">
            <directory suffix="Test.php">./tests/Unit</directory>
        </testsuite>
        <testsuite name="Feature">
            <directory suffix="Test.php">./tests/Feature</directory>
        </testsuite>
    </testsuites>
    <coverage processUncoveredFiles="true">
        <include>
            <directory suffix=".php">./app</directory>
        </include>
        <report>
            <html outputDirectory="reports/coverage"/>
        </report>
    </coverage>
    <php>
        <server name="APP_ENV" value="testing"/>
        <server name="BCRYPT_ROUNDS" value="4"/>
        <server name="CACHE_DRIVER" value="array"/>
        <server name="DB_CONNECTION" value="mysql"/>
        <server name="DB_HOST" value="localhost"/>
        <server name="DB_DATABASE" value="mysql_test"/>
        <server name="DB_USERNAME" value="root"/>
        <server name="MAIL_MAILER" value="array"/>
        <server name="QUEUE_CONNECTION" value="sync"/>
        <server name="SESSION_DRIVER" value="array"/>
        <server name="TELESCOPE_ENABLED" value="false"/>
    </php>
</phpunit>
Run Code Online (Sandbox Code Playgroud)

这是一个已知的问题?或者我错过了什么?

Fra*_*ser 11

问题是您的测试包含隐式提交,因此结束了活动事务。

关于 -

“一旦我执行 CREATE 语句,我就会收到事务错误。” -

CREATE TABLE等是导致隐式提交的语句

这反过来意味着该RefreshDatabase特征将不起作用,因为事务关闭时无法回滚。

因此PDOException: There is no active transaction

这似乎是 php 8.0 的一个已知问题/引发的错误 - https://github.com/laravel/framework/issues/35380


Era*_*els 7

我根据我遇到的情况弄清楚了。我正在使用一个库,它使用迁移逻辑来执行数据迁移(因此$this->artisan('migrate-data');在我的代码中)。
使用该RefreshDatabase特征时,迁移会执行一次,然后启动事务。迁移逻辑对事务做了一些处理,我认为之后关闭它们,导致了我遇到的错误。

对我有用的解决方案是在开始事务之前覆盖RefreshDatabase特征以执行数据迁移:

<?php

namespace Tests;

use Illuminate\Contracts\Console\Kernel;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\RefreshDatabaseState;

trait RefreshDatabaseWithData
{
    use RefreshDatabase;

    protected function refreshTestDatabase(): void
    {
        if (! RefreshDatabaseState::$migrated) {
            $this->artisan('migrate:fresh', $this->migrateFreshUsing());
            $this->artisan('migrate-data'); // << I added this line

            $this->app[Kernel::class]->setArtisan(null);

            RefreshDatabaseState::$migrated = true;
        }

        $this->beginDatabaseTransaction();
     }
}
Run Code Online (Sandbox Code Playgroud)

这仅在 Laravel 9 中进行了测试,但我不明白为什么这在以前的版本上不起作用。