我正在尝试使用Pest在我的Laravel 8项目中引入测试。该项目使用自己的数据库,但也连接到另一个数据库。后者与不同的系统共享,通过在config/database.php
中设置两个连接来完成,即使两个数据库都驻留在生产环境中的同一个MariaDB示例中。
配置/数据库. php:
(...)
'default' => env('DB_CONNECTION', 'mysql'),
'another' => 'another',
'connections' => [
'mysql' => [
'driver' => env('DATABASE_DRIVER', 'mysql'),
'url' => env('DATABASE_URL'),
'host' => env('DB_HOST', '127.0.0.1'),
'port' => env('DB_PORT', '3306'),
'database' => env('DB_DATABASE', 'laravel'),
'username' => env('DB_USERNAME', 'forge'),
'password' => env('DB_PASSWORD', ''),
'unix_socket' => env('DB_SOCKET', ''),
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'prefix' => '',
'prefix_indexes' => true,
'strict' => true,
'engine' => 'innodb',
'options' => extension_loaded('pdo_mysql') ? array_filter([
PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
]) : [],
],
'dummyname' => [
'driver' => env('DATABASE_DRIVER', 'mysql'),
'url' => env('DATABASE_URL'),
'host' => env('DB_HOST_ANOTHER', '127.0.0.1'),
'port' => env('DB_PORT_ANOTHER', '3306'),
'database' => env('DB_DATABASE_ANOTHER', 'another'),
'username' => env('DB_USERNAME_ANOTHER', 'forge'),
'password' => env('DB_PASSWORD_ANOTHER', ''),
'unix_socket' => env('DB_SOCKET', ''),
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'prefix' => '',
'prefix_indexes' => true,
'strict' => true,
'engine' => null,
'options' => extension_loaded('pdo_mysql') ? array_filter([
PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
]) : [],
'modes' => [
'STRICT_TRANS_TABLES',
'NO_ZERO_IN_DATE',
'NO_ZERO_DATE',
'ERROR_FOR_DIVISION_BY_ZERO',
'NO_AUTO_CREATE_USER',
'NO_ENGINE_SUBSTITUTION'
],
],
(...)
由于表名不重叠,并且我使用MySQL数据库中的透视表在它们之间创建了一个关系,因此我决定在测试中使用单个SQLite数据库,方法是设置phpunit.xml
,如下所示:
<php>
<server name="APP_ENV" value="testing"/>
<server name="BCRYPT_ROUNDS" value="4"/>
<server name="CACHE_DRIVER" value="array"/>
<server name="DB_DATABASE" value="/tmp/tests.sqlite"/>
<server name="DB_DATABASE_ANOTHER" value="/tmp/tests.sqlite"/>
<server name="DATABASE_DRIVER" value="sqlite"/>
<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>
有两个名为Project
和PollProject
的模型。第一个是Laravel的,另一个的表在另一个数据库中:
mysql.projects:
- id
- ...
another.poll_projects
- id
- ...
mysql.project_poll_project
- project_id
- poll_project_id
(UNIQUE index on both)
(FOREIGN KEY constraint on the first one)
关系定义如下:
class Project extends Model
{
protected $connection = 'mysql';
public function pollProjects() {
$database = $this->getConnection()->getDatabaseName() . '.';
if ($database == '/tmp/tests.sqlite.') $database = '';
return $this->belongsToMany(PollProject::class, $database . 'project_poll_project', 'project_id', 'poll_project_id');
}
}
class PollProject extends Model
{
// database table settings
protected $connection = 'another';
protected $table = 'poll_projects';
public function projects() {
$foreign_connection = 'mysql';
return $this->belongsToMany(Project::class, "$foreign_connection.project_poll_project", 'poll_project_id', 'project_id');
}
}
我希望基于用户被分配到项目测试权限我通过将RefreshDatabase
附加到TestCase
来刷新数据库
测试/测试案例. php:
abstract class TestCase extends BaseTestCase
{
use CreatesApplication, RefreshDatabase;
}
因为Project
可以有多个PollProjects
,所以我将创建对象的任务委托给工厂,然后尝试将PollProject
附加到Project
。
测试/功能/投票项目角色测试. php:
(...)
beforeEach(function () {
PollProject::factory()->count(2)->create();
$this->accessiblePollProject = PollProject::find(1);
$this->forbiddenPollProject = PollProject::find(2);
Project::factory()->count(1)->create();
$this->project = Project::find(1);
$this->project->pollProjects()->attach($this->accessiblePollProject);
die('It will not reach this point');
(...)
});
it('allows to list templates', function () {
expect(true)->toBeTrue();
});
创建了这个测试用例和其他测试用例(使用Pest\Laravel\get
或test can()
)后,运行测试套件会导致SQLite的锁定数据库错误:
root@5ea0190680a9:/var/www# php artisan test
WARN Tests\Unit\TemplatePolicyTest
✓ it allows superadmins to do anything
! it disallows viewing templates → This test did not perform any assertions /var/www/vendor/pestphp/pest/src/Factories/TestCaseFactory.php(223) : eval()'d code:5
! it disallows updating templates → This test did not perform any assertions /var/www/vendor/pestphp/pest/src/Factories/TestCaseFactory.php(223) : eval()'d code:5
! it disallows destroying templates → This test did not perform any assertions /var/www/vendor/pestphp/pest/src/Factories/TestCaseFactory.php(223) : eval()'d code:5
! it disallows ... templates → This test did not perform any assertions /var/www/vendor/pestphp/pest/src/Factories/TestCaseFactory.php(223) : eval()'d code:5
PASS Tests\Feature\PagesStatusesTest
✓ get '/' → assertRedirect '/home'
✓ get '/home' → assertRedirect '/login'
✓ get '/login' → assertSuccessful
✓ get '/admin/user' → assertRedirect '/login'
FAIL Tests\Feature\PollProjectRolesTest
⨯ it allows to list templates
---
• Tests\Feature\PollProjectRolesTest > it allows to list document templates
Illuminate\Database\QueryException
SQLSTATE[HY000]: General error: 5 database is locked (SQL: insert into "project_poll_project" ("poll_project_id", "project_id") values (1, 1))
at vendor/laravel/framework/src/Illuminate/Database/Connection.php:712
708▕ // If an exception occurs when attempting to run a query, we'll format the error
709▕ // message to include the bindings with SQL, which will make this exception a
710▕ // lot more helpful to the developer instead of just the database's errors.
711▕ catch (Exception $e) {
➜ 712▕ throw new QueryException(
713▕ $query, $this->prepareBindings($bindings), $e
714▕ );
715▕ }
716▕ }
+8 vendor frames
9 tests/Feature/PollProjectRolesTest.php:27
Illuminate\Database\Eloquent\Relations\BelongsToMany::attach(Object(App\PollProject))
Tests: 1 failed, 4 risked, 5 passed
Time: 63.85s
我尝试使用单独的数据库文件,但结果是:
SQL状态[HY 000]:一般错误:1无此表:项目调查项目
与Laravel相同位置的错误在查询MySQL时无法访问数据库another
。:memory:
作为数据库位置也导致no such table
。尽管两个数据库都设置为:memory:
,但它们似乎是单独的示例。As explained:
使用URI格式指定共享高速缓存(如您所使用的)将导致同一进程中的所有连接使用同一数据库
这是否意味着有多个进程处理连接?或者Laravel使用某种连接池?当我从已经应用了迁移的TestCase
中删除RefreshDatabase
trait时,测试设置通过了:
root@5ea0190680a9:/var/www# php artisan test
WARN Tests\Unit\DocumentTemplatePolicyTest
✓ it allows superadmins to do anything
! it disallows viewing templates → This test did not perform any assertions /var/www/vendor/pestphp/pest/src/Factories/TestCaseFactory.php(223) : eval()'d code:5
! it disallows updating templates → This test did not perform any assertions /var/www/vendor/pestphp/pest/src/Factories/TestCaseFactory.php(223) : eval()'d code:5
! it disallows destroying templates → This test did not perform any assertions /var/www/vendor/pestphp/pest/src/Factories/TestCaseFactory.php(223) : eval()'d code:5
! it disallows ... templates → This test did not perform any assertions /var/www/vendor/pestphp/pest/src/Factories/TestCaseFactory.php(223) : eval()'d code:5
PASS Tests\Feature\PagesStatusesTest
✓ get '/' → assertRedirect '/home'
✓ get '/home' → assertRedirect '/login'
✓ get '/login' → assertSuccessful
✓ get '/admin/user' → assertRedirect '/login'
It will not reach this point
1条答案
按热度按时间xytpbqjk1#
我意识到,虽然可能没有连接池,但必须至少涉及两个连接,因为我自己在
config/database.php
中定义了这些连接。我通过在使用连接的地方参数化连接名称来解决这个问题,因此在测试中只使用一个连接。我在
phpunit.xml
文件中创建了一个附加变量,因此只使用默认连接:在我更改的配置文件中:
至
每次出现
DB::connection('another')
或Schema::('another')
时,分别变更为DB::connection(config('database.another'))
和Schema::connection(config('database.another'))
。由于模型也定义了
private $connection = 'another';
,而且这个属性是静态的,所以我必须在那里定义一个允许动态值的构造函数:这为我解决了问题。