“没有主动交易”在Laravel 8.0测试中刷新数据库时
使用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
并且由于已经插入了数据并且在测试后未清除该数据后的测试失败。
我要做的测试是:
<?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' => '[email protected]'
]);
$this->app->bind(UserRepository::class, function() use ($user) {
return new UserRepository($user, new Company());
});
$loginResponse = $this->post('/api/login', [
'email' => '[email protected]',
'password' => 'password'
]);
$loginResponse->assertStatus(200);
$loginResponse->assertJsonStructure([
'data' => [
'user' => [
'name',
'surname',
'email',
'abilities'
],
'token',
]
]);
}
}
执行此测试并在数据库中检查后,数据仍然存在。有或没有行受保护的数组$ ConnectionStotRansact = ['mySql'];
给出了相同的结果。
我的phpunit.xml
- file看起来像:
<?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>
这是一个已知问题吗?还是我想念某个时候?
Using php 8.0.2 and Laravel 8.37.0, I am running tests where for every test the database data should be refreshed, since there is conflicting data per test (due to unique constraints).
using the in-memory database with SQLite, this works, but when I switch to MySQL (v8.0.23) I get the next error:
1) Tests\Feature\Controllers\AuthControllerTest::testSuccessLogin
PDOException: There is no active transaction
and the tests after this one fail due to data already inserted and not cleared after the test.
The the test that I am trying to do is:
<?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' => '[email protected]'
]);
$this->app->bind(UserRepository::class, function() use ($user) {
return new UserRepository($user, new Company());
});
$loginResponse = $this->post('/api/login', [
'email' => '[email protected]',
'password' => 'password'
]);
$loginResponse->assertStatus(200);
$loginResponse->assertJsonStructure([
'data' => [
'user' => [
'name',
'surname',
'email',
'abilities'
],
'token',
]
]);
}
}
and after executing this test and checking in the database, the data still exists. With and without the line protected array $connectionsToTransact = ['mysql'];
gives me the same result.
My phpunit.xml
-file look like:
<?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>
Is this a known issue? Or am I missing someting?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
问题是您的测试包含隐式提交,因此结束了主动交易。
关于 -
创建表
等隐含的提交。反过来,这意味着
RefreshDatabase
性状将无法使用,因为当交易关闭时不可能回滚。因此,
pdoException:没有主动事务
,似乎是已知的问题/抛出的错误,php 8.0- https://github.com/laravel/framework/issues/35380
The issue is that your test contains implicit commits and so ends the active transaction.
Re -
CREATE TABLE
etc are statements that cause an implicit commit.This in turn means the
RefreshDatabase
trait will not work because a rollback is not possible when the transaction is closed.Hence
PDOException: There is no active transaction
It seems to be a known issue/thrown error with php 8.0 - https://github.com/laravel/framework/issues/35380
我想知道的是我的情况。我正在使用库的库使用迁移逻辑来执行数据迁移(因此,我的代码中的
$ this-&gt; artisan('migrate-data');
在我的代码中)。当使用
RefreshDatabase
特征时,一次又一次执行迁移,开始进行事务。迁移逻辑对交易有所作为,我认为之后将它们关闭,从而导致我遇到的错误。对我有用的解决方案是覆盖
RefreshDatabase
特征以在开始交易之前执行数据迁移:仅在Laravel 9中进行测试,但我不明白为什么这不起作用在以前的版本上。
I figured it out for the case I was having. I am using a library wich uses the migration logic to execute data migrations (hence the
$this->artisan('migrate-data');
in my code).When using the
RefreshDatabase
trait, migrations are executed once and after that, a transaction is started. The migration logic does something with transactions, I think closing them afterwards, causing the error I was having.The solution that worked for me, was to overwrite the
RefreshDatabase
trait to execute the data migrations before starting the transaction:This is tested in Laravel 9 only, but I don't see why this wouldn't work on previous versions.
这就是我解决的方式:
步骤1:扩展连接类和覆盖事务方法:
替代基本上会在内部禁用交易,使您的当前代码保持完整。
步骤2:通过ServiceProvider(App/Provers/AppServiceProvider.php)将该混凝土类绑定到抽象连接系数是一个好地方):
请注意,绑定在对环境的条件内。我们只想在运行测试时仅应用此绑定。
如果您不使用.env.testing文件进行单元测试,我真的建议您这样做。它使一切都更加干净。您可以将.env复制到.env.testing,并且只能更新DB_Connection和DB_DATABASE常数,指向您的测试数据库。
然后在phpinit.xml中定义.env.testing环境:
请注意,由于这些参数将从.env.testing获取,因此我评论了DB_Connection和db_database
This is how I solved it:
Step 1: Extend Connection class and override transaction method:
This override basically disables transactions internally, keeping your current code intact.
Step 2: Bind that concrete class to the abstract ConnectionInterface via a ServiceProvider (app/Providers/AppServiceProvider.php would be a good place):
Notice that the binding is inside a condition against the environment. We want to apply this binding only when running tests.
If you're not using a .env.testing file for Unit Testing, I really recommend you to do it. It keeps everything much more cleaner. You can just copy .env to .env.testing and only update the DB_CONNECTION and DB_DATABASE constants, pointing to your test database.
Then define the .env.testing environment in phpinit.xml:
Notice that I commented out DB_CONNECTION AND DB_DATABASE since those parameters will be fetched from .env.testing