使用php 8.0.2和Laravel8.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' => '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',
]
]);
}
}在执行测试并在数据库中检查之后,数据仍然存在。不管有没有行,protected array $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>这是众所周知的问题吗?还是我错过了什么?
发布于 2022-06-08 09:42:43
我想出来是为了我的案子。我正在使用一个使用迁移逻辑的库来执行数据迁移(因此在我的代码中使用了$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();
}
}这是只在Laravel 9测试,但我不明白为什么这将不能在以前的版本。
发布于 2022-04-06 04:33:41
我就是这样解决的:
步骤1:扩展连接类和覆盖事务方法:
use Illuminate\Database\Connection as DBConnection;
use Closure;
class Connection extends DBConnection
{
public function transaction(Closure $callback, $attempts = 1)
{
$callback($this);
}
}这种覆盖基本上会在内部禁用事务,从而保持当前代码的完整。
步骤2:通过一个ServiceProvider将该具体类绑定到抽象ConnectionInterface (app/Providers/AppServiceProvider.php将是一个很好的地方):
if(config('app.env')==='testing') {
$this->app->bind(ConnectionInterface::class, Connection::class);
}请注意,绑定位于针对环境的条件内。我们只希望在运行测试时应用此绑定。
如果您没有使用.env.testing文件进行单元测试,我建议您这样做。它能让一切变得更干净。您只需将.env复制到.env.testing,并且只更新指向测试数据库的DB_CONNECTION和DB_DATABASE常量。
然后在.env.testing中定义phpinit.xml环境:
<php>
<env name="APP_ENV" value="testing"/>
<!-- <env name="DB_CONNECTION" value="memory_testing"/> -->
<!-- <env name="DB_DATABASE" value=":memory:"/> -->
</php>注意,我注释掉了DB_CONNECTION和DB_DATABASE,因为这些参数将从.env.testing中获取。
https://stackoverflow.com/questions/67198158
复制相似问题