Laravel 测试不会在每次测试后回滚事务

Posted

技术标签:

【中文标题】Laravel 测试不会在每次测试后回滚事务【英文标题】:Laravel tests don't rollback transaction after each test 【发布时间】:2018-07-21 08:36:33 【问题描述】:

我的测试使用 trait RefreshDatabase 在开始测试之前“迁移新鲜”并为每种测试方法使用事务。

迁移工作正常,但事务根本不工作。

我尝试公开我的设置:

MariaDB 10.1 在 docker 容器中运行(我已经证明我的测试中使用的所有表都在 InnoDB 中,因此支持事务) 基础测试类使用RefreshDatabase 我尝试使用单独的连接与$connectionsToTransact 一起进行测试,并使用默认连接进行测试。交易也不行

我的设置方法:

protected function setUp()

    parent::setUp();

    Queue::fake();

您可以在此 Gist 中找到完整的 Test Class 和 Test 基类: https://gist.github.com/patriziotomato/e25de1e160dace08edefa682b64bd150

我已经尝试调试,并且还归结为 PDO 启动和回滚事务,所以看起来 laravel 代码尝试进行事务和回滚,但它对我的测试没有任何影响。

我需要想法还有什么可能出错的地方

【问题讨论】:

您是否在需要时致电setUp()tearDown() 将我的setUp() 方法添加到我原来的问题中 你在开发一个包吗?为什么不使用sqlite 而不是适当的数据库? 在最新版本的 Laravel 5.6 中修复了一个与测试中的 RefreshDatabase 相关的错误。尝试升级到最新版本的 5.6 您指的是哪个修复程序? 【参考方案1】:

您可能正在使用Model::truncate()

不幸的是,truncate()mysql 5.1.32 以来的事务不“兼容”。您可以 DROP 表,但不能在事务中 truncate()

http://dev.mysql.com/doc/refman/5.1/en/truncate-table.html

根据这个 URL,从 MySQL 5.1.32 开始,TRUNCATE TABLE 是 DDL 和 不像 DELETE 那样 DML。这意味着 TRUNCATE TABLE 将导致 事务块中间的隐式 COMMIT。所以,使用 DELETE FROM 在您需要清空而不是 TRUNCATE TABLE 的表上。

来自 *** 和 Laracasts 的相关答案:

https://laracasts.com/discuss/channels/eloquent/db-transaction-doesnt-appear-to-rollback-properly https://***.com/a/5972738/667773

【讨论】:

否认你是英雄 DELETE FROM 是否像截断一样重置 ID 列? delete兼容事务,只有truncate不兼容【参考方案2】:

我自己也遇到了类似的 MySQL 设置问题。我还从上面尝试了 Anthony 的解决方案,我也看到了相同的 ...1305 SAVEPOINT trans2 does not exist... 错误。

在我的例子中,罪魁祸首是代码中的Model::truncate() 操作(用于重新导入命令)。不知何故,这似乎扰乱了 Laravel 的事务/回滚处理(或 MySQL 的?),导致上述错误。改用Model::all()->each->delete() 解决了我的问题。 (经过进一步测试,似乎我也无法重置 auto_increment 值,所以这就是问题所在......)

值得注意的是,如果使用内存数据库但使用 MySQL 设置可能不会发生这种情况,例如,如果恶意条目保持完整,很容易与即将进行的测试混淆,从而导致难以调试的错误,所以要小心... :)

更新 最佳答案on this Laracast thread实际上解释了事务操作在操作过程中有一个隐式提交,并且在测试过程中抛出了事务堆栈。

【讨论】:

这可能取决于 Laravel 版本,但我认为 Model::all()->each->delete() 不会起作用。具体来说,each 是一个接受回调的函数,而不是一个属性,除非我弄错了。【参考方案3】:

我也有同样的问题。从未找到确切原因,但有一个解决方法 - 手动启动和回滚事务:

public function setUp()

    parent::setUp();
    DB::beginTransaction();


public function tearDown()

    DB::rollback();
    parent::tearDown();

【讨论】:

就我而言,我猜是因为RefreshDatabase 这不起作用:Doctrine\DBAL\Driver\PDOException : SQLSTATE[42000]: Syntax error or access violation: 1305 SAVEPOINT trans2 does not exist【参考方案4】:

要回滚测试文件中的事务,您可以使用DatabaseTransactions

...
use Illuminate\Foundation\Testing\DatabaseTransactions;
...

class SomeTest extends TestCase 

    use DatabaseTransactions;

public some_assertion_method()

    ...

【讨论】:

嗨。 RefreshDatabase 也可以这样做,但在我的情况下它们不起作用。我也尝试了你的想法,但交易不想在这里工作:(【参考方案5】:

更改文件 phpunit.xml

<php>
        <env name="DB_CONNECTION" value="mysq"/>
        <env name="APP_ENV" value="local"/>
        <env name="CACHE_DRIVER" value="array"/>
        <env name="SESSION_DRIVER" value="array"/>
        <env name="QUEUE_DRIVER" value="sync"/>
        <env name="MAIL_DRIVER" value="array"/>
        <env name="SMS_DRIVER" value="array"/>
</php>

将此特征添加到测试文件:

use DatabaseTransactions;

【讨论】:

以上是关于Laravel 测试不会在每次测试后回滚事务的主要内容,如果未能解决你的问题,请参考以下文章

使用 Atomikos JTA 事务管理器时,Spring 单元测试不会回滚

Spring Test : 测试 dao 后回滚数据库

@Test 后回滚事务

Spring 的 TestNG 回滚事务不起作用

数据库单元测试框架?

使用 Jooq 进行集成测试