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 测试不会在每次测试后回滚事务的主要内容,如果未能解决你的问题,请参考以下文章