Laravel RefreshDatabase 特性未将每个测试包装在事务中

问题描述 投票:0回答:1

RefreshDatabase 文档指出

如果您的架构是最新的,则

Illuminate\Foundation\Testing\RefreshDatabase
特征不会迁移您的数据库。相反,它只会在数据库事务中执行测试。

我使用的是 Laravel 10,我认为它在我的项目中无法正常工作。

问题#1

由于项目迁移需要大约 1 分钟才能执行(

php artisan migrate:fresh --env testing
),我有一个本地测试数据库,我会保持最新状态以快速运行测试,但测试执行总是花费 至少 1 分钟,就好像它正在运行所有在运行测试之前进行迁移。

问题#2

我需要测试一段代码是否落入

catch(Exception $e)
分支,并且由于这段代码从数据库检索结果,我想在所有
setUp
之后更改表名称以使其抛出异常,并且工作了
问题是,现在测试同一段代码的同一测试类中的每个后续测试都会失败,并显示消息
SQLSTATE[42S02]: Base table or view not found
:这意味着该表仍然具有新的错误名称,因此看起来它不是要进行的单个测试包装在迁移中,但所有测试都在同一个测试类中,但这与之前的数据库操作(例如正在创建的实例)冲突,不影响后续测试。

我真的不知道哪里出了问题。

php laravel testing phpunit laravel-10
1个回答
0
投票

我将其作为答案而不是评论,以便我可以放置代码并使其更加明显:

关于文档具有误导性,您是正确的,请记住,您原来的帖子链接的是 Laravel 11.x 文档而不是 10.x(我已经修复了该问题),但他们仍然说同样的事情。

这是 Laravel 10 的 源代码

public function refreshDatabase()
{
    $this->beforeRefreshingDatabase();

    $this->usingInMemoryDatabase()
                    ? $this->refreshInMemoryDatabase()
                    : $this->refreshTestDatabase();

    $this->afterRefreshingDatabase();
}

如您所见,您有一个

before
钩子、迁移运行和一个
after
钩子。两个钩子都是空的,您可以在测试中添加代码来干扰该特征。

如果检查

refreshTestDatabase()
方法执行的内容:

protected function refreshTestDatabase()
{
    if (! RefreshDatabaseState::$migrated) {
        $this->artisan('migrate:fresh', $this->migrateFreshUsing());

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

        RefreshDatabaseState::$migrated = true;
    }

    $this->beginDatabaseTransaction();
}

你可以看到它正在利用

RefreshDatabaseState
,它是一个简单的静态类,并且没有预先设置或以任何方式操作。因此
RefreshDatabaseState::$migrated
在第一次运行时始终为
false
,因此它将运行其中的代码,在本例中为
migrate:fresh

我从未采用过你的方法,但你可以尝试加入

before
钩子,然后手动检查是否运行了所有迁移,如下所示:

// In your test define exactly this
protected function beforeRefreshingDatabase()
{
    $this->artisan('migrate');

    \Illuminate\Foundation\Testing\RefreshDatabaseState::$migrated = true;
}

通过这种方式,我们可以自动迁移数据库(如果缺少任何运行),然后我们告诉特征它已经完成,无需

migrate:fresh
。请记住,它可能无法像运行时那样正常工作
$this->app[Kernel::class]->setArtisan(null);
,我不知道为什么,而且我真的不想运行它。

我想过使用

migrate:status
并检查是否所有迁移都已运行,但如果缺少任何状态代码,它不会给我们状态代码,因此比较困难,因为您需要读取输出并检查它,而不是真的很想要。


简化的解决方案是:

  • 如果您想在测试中使用
    migrate
    命令自动运行缺失的迁移,只需将该命令添加到
    setUp
    ,它可以在基类上或在该特定测试上
  • 如果您不关心在测试运行时自动运行缺失的迁移(仅在第一次),那么您可以直接避免使用原始代码,只需使用
    \Illuminate\Foundation\Testing\DatabaseTransactions
    作为
    RefreshDatabase
    ,在我们的代码之后,正是使用在另一个特征中,只是开始一个交易

如果您想要此解决方案,只需在测试中包含

DatabaseTransactions
,但请记住在基本测试类中的
migrate
上自动运行
setUp
,或者始终通过 CLI 保持最新状态,就是这样!

© www.soinside.com 2019 - 2024. All rights reserved.