使用Laravel黄昏只迁移一次

问题描述 投票:6回答:4

根据to the "Database Testing" documentation,我可以在每次测试后重置数据库(第一个选项)。第二个选项是使用Transactions运行测试。对我来说这似乎是一种更好的方法,但如果我想使用事务运行,则迁移不会运行。

有没有办法在所有测试过程中运行一次迁移?

换句话说,我想运行迁移,使用事务运行每个测试,然后回滚。我尝试了文档说的内容,但我认为缺少了一些东西。

php laravel unit-testing laravel-5.4 laravel-dusk
4个回答
3
投票

今天与此纠缠了一段时间,并且与迁移一起运行迁移似乎可以解决问题。我的测试快照如下:

<?php

namespace Tests\Browser;

use App\User;
use Tests\DuskTestCase;

use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;

class DefaultTest extends DuskTestCase
{
    use DatabaseMigrations, DatabaseTransactions;

    /**
     * A Dusk test example.
     *
     * @return void
     */
    public function test_something()
    {
        //Add test stuff here
    }
}

在我的实际测试中我有几个工厂,他们似乎在测试后按预期运行数据销毁。


2
投票

目前无法与黄昏一起运行DatabaseTransactions。

https://github.com/laravel/dusk/issues/110

用户记录的创建及其在浏览器中的使用是在两个不同的过程中完成的。这意味着创建的用户是未提交的数据库事务的一部分,因此浏览器进程无法访问。

数据库迁移工作。所以你应该使用那些。还要确保运行单独的测试数据库,这样就不会弄乱生产/开发数据库。

https://laravel.com/docs/5.4/dusk#environment-handling

要在运行测试时强制Dusk使用自己的环境文件,请在项目的根目录中创建.env.dusk。{environment}文件。例如,如果要从本地环境启动dusk命令,则应创建.env.dusk.local文件。

运行测试时,Dusk将备份.env文件并将您的Dusk环境重命名为.env。测试完成后,将恢复.env文件。

提供的答案有效,因为DatabaseMigrations有效。 use DatabaseTransactions无关紧要。


1
投票

根据我的理解,我不认为使用黄昏时交易可以正常工作,因为黄昏时每个浏览器请求都会创建一个单独的laravel应用程序实例。

以前,phpunit会在内存中创建一个新的应用程序作为该过程的一部分(在setUp / createApplication方法中),然后测试该测试应用程序,然后销毁它并设置下一个。因此,在为下一个测试启动新数据库连接之前,事务可以包围(或仅在内部)创建和销毁该应用程序的部分。

黄昏时分,这是真正的端到端测试(包括浏览器,伪造的用户交互,本地机器上的路由等),这意味着测试运行的环境并非全部都包含在内,就像它们一样通常是在phpunit。

黄昏做了以下事情:

  • 复制你的.env.dusk.*并启动chromedriver(或你使用的任何类似硒的东西)
  • 触发phpunit shell命令(即新命令,新进程)
  • phpunit命令运行你的黄昏测试,每个测试打开一个浏览器窗口并发出请求(每个请求启动一个新的php-fpm和php进程(对于nginx)) - 就像你自己提出这些请求一样。它们中的每一个都与db具有单独的连接,因此不能与彼此的事务交互。

同样值得注意的是,DatabaseTransactions特性是在基金会包中,而不是Dusk包,所以它不是用Dusk构建/打包的。

这也解释了为什么内存中的sqlite不适用于黄昏,因为一个进程无法访问另一个进程的内存。


0
投票

这是一种非常便携和可重用的方式:

abstract class DuskTestCase extends BaseTestCase {

   ...

    /**
     * @param int $batchCounter
     * @param string $className
     * @param int $threshold
     */
    public function refreshDb(&$batchCounter, $className = '', $threshold = 0) {
        if ($batchCounter <= $threshold) {
            //TODO: Here is where you'll want to run migrations and seeds and whatnot.
            $batchCounter++;
            $this->consoleOutput(trim($className . ' called refreshAndSeedTestingDb and $batchCounter++. $batchCounter=' . $batchCounter));
        }
    }

   /**
    * @param string $msg
    */
   public function consoleOutput($msg) {
       Log::debug($msg);
       $output = new \Symfony\Component\Console\Output\ConsoleOutput();
       $output->writeln($msg);
   }

然后在每个测试文件中:

class ExampleBrowserTest extends DuskTestCase {

    protected static $countDbRefreshed = 0;

    public function setUp() {//runs before every test function in this class
        parent::setUp();
        $this->refreshDb(self::$countDbRefreshed, __CLASS__); //inside uses a property to run only once per class
    }
...
© www.soinside.com 2019 - 2024. All rights reserved.