繁体   English   中英

Laravel 测试从架构迁移数据库

[英]Laravel tests database migration from schema

问题

  • 我正在使用 Laravel 8.83.23
  • 我在database\schema\mysql-schema.dump中有压缩迁移的模式转储
  • 测试在测试数据库之上运行,如database.php

    'testing' => [
                    'driver' => 'mysql',
                    'host' => env('DB_TEST_HOST', '127.0.0.1'),
                    'port' => env('DB_TEST_PORT', '3306'),
                    'database' => env('DB_TEST_DATABASE', 'forge'),
                    'username' => env('DB_TEST_USERNAME', 'forge'),
                    'password' => env('DB_TEST_PASSWORD', ''),
                ],

  • 在我压缩迁移之前,我的测试用例仅使用DatabaseMigrations特征,并且每次都重新创建测试数据库并且一切正常,测试 class 的示例:

    class SystemControllerTest extends TestCase
    {
        use WithFaker;
        use DatabaseMigrations;
        /**
         * @var User
         */
        private $user;
    
        public function setUp(): void
        {
            parent::setUp();
    
            //create roles and data
            $this->seed(RoleAndPermissionSeeder::class);
        ... etc

  • 找到并执行迁移,重新创建数据库
  • 然后,我压缩了迁移,所以所有迁移都被删除了,我得到了database\schema\mysql-schema.dump
  • php artisan migrate通过命令行按预期工作,从转储创建完整的数据库模式(它找到它)
  • 但是测试不再起作用,因为有一个错误

    SQLSTATE[42S02]: Base table or view not found: 1146 Table 'cinema_test.roles' doesn't exist (SQL: delete from `roles`)

  • 当我在测试运行后检查 sql 测试数据库时,它是空的(只有表migrations在那里创建,它是空的)
  • 即使我在测试设置中调用artisan migrate ,此错误仍然存在:

    public function setUp(): void
        {
            parent::setUp();
            Artisan::call('migrate', array(
       '--database' => 'testing',
       '--force' => true));
            //it crashes here
            $this->seed(RoleAndPermissionSeeder::class);

  • RoleAndPermissionSeeder仅使用不存在的 sql 表进行操作,因此出现错误
  • 我什至尝试了DatabaseMigrationsDatabaseTransactionsRefreshDatabase特征,但没有任何成功
  • 如何填充数据库数据? 我没有办法阅读Artisan::call('migrate')命令的 output,所以我不知道那里发生了什么
  • Artisan::call('migrate')的返回码为0
  • 我可能缺少一些设置吗?

似乎在测试时模式转储不能用于内存数据库

https://laravel.com/docs/9.x/migrations#squashing-migrations

也许可以做这样的事情

DB::unprepared(file_get_contents("path/file.sql"));

只会尝试作为最后的手段,个人想要测试迁移,如果你采用这种方法,你也应该添加一个测试迁移的检查

我终于想通了。

问题的原因

问题在于测试环境的设置不正确。 我还没有找到确切的原因,但我想出了如何设置测试环境以便找到并加载转储。

我如何追捕这个错误

这描述了我如何找到解决此问题的方法的步骤。

database.php我复制了测试数据库而不是普通数据库
  • database.php我有主数据库连接:

    'mysql' => [
                'driver' => 'mysql',
                'url' => env('DATABASE_URL'),
                'host' => env('DB_HOST', '127.0.0.1'),
                'port' => env('DB_PORT', '3306'),
                'database' => env('DB_DATABASE', 'forge'),
                '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' => false,
                'engine' => null,
                'options' => extension_loaded('pdo_mysql') ? array_filter([
                    PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
                ]) : [],
            ],

和测试连接


    'testing' => [
                    'driver' => 'mysql',
                    'host' => env('DB_TEST_HOST', '127.0.0.1'),
                    'port' => env('DB_TEST_PORT', '3306'),
                    'database' => env('DB_TEST_DATABASE', 'forge'),
                    'username' => env('DB_TEST_USERNAME', 'forge'),
                    'password' => env('DB_TEST_PASSWORD', ''),
                ],

  • 我将testing连接数据复制到一个新的mysql连接中,只是为了看看,是否在命令行上我得到相同的结果
  • 所以,文件看起来像这样

    'mysql' => [
                'url' => env('DATABASE_URL'),
                'driver' => 'mysql',
                    'host' => env('DB_TEST_HOST', '127.0.0.1'),
                    'port' => env('DB_TEST_PORT', '3306'),
                    'database' => env('DB_TEST_DATABASE', 'forge'),
                    'username' => env('DB_TEST_USERNAME', 'forge'),
                    'password' => env('DB_TEST_PASSWORD', ''),
                'unix_socket' => env('DB_SOCKET', ''),
                'charset' => 'utf8mb4',
                'collation' => 'utf8mb4_unicode_ci',
                'prefix' => '',
                'prefix_indexes' => true,
                'strict' => false,
                'engine' => null,
                'options' => extension_loaded('pdo_mysql') ? array_filter([
                    PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
                ]) : [],
            ],
    
    /*'testing' => [
                    'driver' => 'mysql',
                    'host' => env('DB_TEST_HOST', '127.0.0.1'),
                    'port' => env('DB_TEST_PORT', '3306'),
                    'database' => env('DB_TEST_DATABASE', 'forge'),
                    'username' => env('DB_TEST_USERNAME', 'forge'),
                    'password' => env('DB_TEST_PASSWORD', ''),
                ],*/

  • 在控制台上,我运行了php artisan:migrate
  • 找到并加载了数据库转储
  • 因此,在正常情况下发现了转储,但在测试用例中没有发现
  • 经过一番研究,我更改了phpunit.xml中的测试环境设置,我现在解释一下
文件phpunit.xml

phpunit.xml如下(此处未显示完整文件):


       <server name="QUEUE_CONNECTION" value="sync"/>
        <server name="SESSION_DRIVER" value="array"/>
        <server name="TELESCOPE_ENABLED" value="false"/>
        <env name="DB_CONNECTION" value="testing"/>     
    </php>
</phpunit>

  • 所以,我们可以看到测试数据库连接是为单元测试定义的
  • 在 web 上,我发现建议只设置数据库表,而不是更改整个连接进行测试,因为它更容易
  • 我试过这样的方法,所以phpunit.xml变成了
       <server name="QUEUE_CONNECTION" value="sync"/>
        <server name="SESSION_DRIVER" value="array"/>
        <server name="TELESCOPE_ENABLED" value="false"/>
        <env name="DB_DATABASE" value="cinema_test"/>   
    </php>
</phpunit>

  • 我从database.php.env文件中的相关过时变量中删除了测试连接
  • 这解决了这个问题,即使现在在测试中也加载了转储文件
结论

虽然我还没有弄清楚 lavavel 没有加载转储文件的真正原因,但我找到了一种解决方法,即只更改用于测试的数据库名称,而不是为测试目的定义全新的 sql 连接。 这解决了这个问题,并且现在在测试期间加载了数据库转储文件。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM