search

Home  >  Q&A  >  body text

Migrate database to test Laravel's schema

<p><h1>问题</h1> <ul> <li>我正在使用 Laravel <code>8.83.23</code></li> <li>我有来自压缩迁移的模式转储文件,位于 <code>database\schema\mysql-schema.dump</code></li> <li>测试运行在测试数据库上,就像在 <code>database.php</code> 中一样</li> </ul> <pre class="brush:php;toolbar:false;">'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', ''), ],</pre> <ul> <li>在我压缩迁移之前,我的测试用例只使用了 <code>DatabaseMigrations</code> trait,并且测试数据库每次都会被重新创建,一切正常,以下是一个测试类的示例:</li> </ul> <pre class="brush:php;toolbar:false;">class SystemControllerTest extends TestCase { use WithFaker; use DatabaseMigrations; /** * @var User */ private $user; public function setUp(): void { parent::setUp(); //创建角色和数据 $this->seed(RoleAndPermissionSeeder::class); ... 等等</pre> <ul> <li>迁移被找到并执行,重新创建了数据库</li> <li>然后,我压缩了迁移,所以所有的迁移都被删除了,我得到了 <code>database\schema\mysql-schema.dump</code></li> <li><code>php artisan migrate</code> 通过命令行按预期工作,从转储文件中创建完整的数据库模式(它找到了它)</li> <li>然而,测试不再工作,因为出现了一个错误</li> </ul> <pre class="brush:php;toolbar:false;">SQLSTATE[42S02]: Base table or view not found: 1146 Table 'cinema_test.roles' doesn't exist (SQL: delete from `roles`)</pre> <ul> <li>当我在测试运行后检查 sql 测试数据库时,它是空的(只有表 <code>migrations</code> 被创建,而且是空的)</li> <li>即使在测试的设置中调用 <code>artisan migrate</code>,这个错误仍然存在:</li> </ul> <pre class="brush:php;toolbar:false;">public function setUp(): void { parent::setUp(); Artisan::call('migrate', array( '--database' => 'testing', '--force' => true)); //它在这里崩溃 $this->seed(RoleAndPermissionSeeder::class);</pre> <ul> <li><code>RoleAndPermissionSeeder</code> 只操作不存在的 sql 表,因此出现错误</li> <li>我甚至尝试了 <code>DatabaseMigrations</code>、<code>DatabaseTransactions</code> 和 <code>RefreshDatabase</code> traits,但都没有成功</li> <li>我如何填充数据库数据?我无法读取 <code>Artisan::call('migrate')</code> 命令的输出,所以我不知道那里发生了什么</li> <li><code>Artisan::call('migrate')</code> 的返回代码是 <code>0</code></li> <li>我是否可能遗漏了一些设置?</li> </ul></p>
P粉722409996P粉722409996457 days ago475

reply all(2)I'll reply

  • P粉952365143

    P粉9523651432023-08-27 15:05:56

    Looks like you can't use a schema dump file to operate an in-memory database while testing

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

    You can try this

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

    Only try it as a last resort. Personally, it is recommended to migrate in the test environment. If you adopt this method, you should also add a check for migration in the test environment

    reply
    0
  • P粉821274260

    P粉8212742602023-08-27 12:58:12

    I finally figured it out.

    Cause of the problem

    The problem lies in the incorrect settings of the test environment. I didn't find the exact cause, but I found how to set up the test environment so that the dump file is found and loaded.

    How do I track down errors

    This describes the steps I took to find the solution.

    In database.php I copied the test database instead of the normal database
    • In database.php I have the main database connection:
    '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'),
                    ]) : [],
                ],

    and test connection

    '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', ''),
                    ],
    • I copied the testing connection data into a new mysql connection just to see if I got the same results on the command line
    • So, the file now looks like this
    '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', ''),
                    ],*/
    • On the console, I ran php artisan:migrate
    • Database dump file found and loaded
    • So, in normal case the dump file is found, but in test case not found
    • After some research, I changed the settings of the test environment in phpunit.xml, I will explain it now
    Filephpunit.xml

    phpunit.xml looks like this (the complete file is not shown here):

    <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>
    • So, we can see that the test database connection is defined as a unit test
    • On the web I found advice to just set up the database tables instead of changing the entire connection for testing because it's easier
    • I tried something like this, so phpunit.xml became
    <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>
    • I removed the test connection from database.php and removed the related obsolete variables from the .env file
    • This fixes the problem, now the dump file is also loaded in the test
    in conclusion

    While I didn't find the actual cause of Laravel failing to load the dump file, I found a workaround by changing the database name just for testing instead of defining a completely new SQL connection for testing purposes. This fixed the issue and now the database dump file is loaded during testing.

    reply
    0
  • Cancelreply