34

Laravel 的文档建议使用DatabaseMigrationstrait 在测试之间迁移和回滚数据库。

use Illuminate\Foundation\Testing\DatabaseMigrations;

class ExampleTest extends TestCase
{
    use DatabaseMigrations;

    /**
     * A basic functional test example.
     *
     * @return void
     */
    public function testBasicExample()
    {
        $response = $this->get('/');

        // ...
    }
}

但是,我有一些种子数据想用于我的测试。如果我运行:

php artisan migrate --seed

然后它适用于第一个测试,但它无法通过后续测试。这是因为 trait 会回滚迁移,并且当它再次运行迁移时,它不会为数据库做种。如何通过迁移运行数据库种子?

4

7 回答 7

37

您需要做的就是db:seed在 setUp 函数中进行工匠调用

<?php

use Illuminate\Foundation\Testing\DatabaseMigrations;

class ExampleTest extends TestCase
{
    use DatabaseMigrations;

    public function setUp(): void
    {
        parent::setUp();

        // seed the database
        $this->artisan('db:seed');
        // alternatively you can call
        // $this->seed();
    }

    /**
     * A basic functional test example.
     *
     * @return void
     */
    public function testBasicExample()
    {
        $response = $this->get('/');

        // ...
    }
}

参考:https ://laravel.com/docs/5.6/testing#creating-and-running-tests

于 2018-04-15T07:14:47.467 回答
25

我花了一些时间来解决这个问题,所以我想我会分享

如果您查看DatabaseMigrationstrait的源代码,您会发现它有一个runDatabaseMigrations被调用的函数,该setUp函数在每次测试之前运行并注册一个回调以在拆卸时运行。

您可以通过给该函数起别名来“扩展”特征,重新声明一个新函数,其中包含您的逻辑(artisan db:seed)在原始名称下,并在其中调用别名。

use Illuminate\Foundation\Testing\DatabaseMigrations;

class ExampleTest extends TestCase
{
    use DatabaseMigrations {
        runDatabaseMigrations as baseRunDatabaseMigrations;
    }

    /**
     * Define hooks to migrate the database before and after each test.
     *
     * @return void
     */
    public function runDatabaseMigrations()
    {
        $this->baseRunDatabaseMigrations();
        $this->artisan('db:seed');
    }

    /**
     * A basic functional test example.
     *
     * @return void
     */
    public function testBasicExample()
    {
        $response = $this->get('/');

        // ...
    }
}
于 2017-02-20T16:52:39.717 回答
21

使用 Laravel 8,如果您使用的是RefreshDatabasetrait,您可以使用以下方法从您的测试用例中调用播种:

use Illuminate\Foundation\Testing\RefreshDatabase;

class ExampleTest extends TestCase
{
    use RefreshDatabase;

    /**
     * A basic functional test example.
     *
     * @return void
     */
    public function testBasicExample()
    {
        // Run the DatabaseSeeder...
        $this->seed();

        // Run a specific seeder...
        $this->seed(OrderStatusSeeder::class);

        $response = $this->get('/');

        // ...
    }
}

有关更多信息/示例,请参阅文档: https ://laravel.com/docs/8.x/database-testing#running-seeders

于 2021-02-21T23:30:48.130 回答
17

我知道这个问题已经被回答了好几次了,但是我没有看到这个特定的答案,所以我想我会把它扔进去。

在 laravel 中(至少从 v5.5 开始)有一段时间,TestCase类中有一个方法专门用于调用数据库播种器:

https://laravel.com/api/5.7/Illuminate/Foundation/Testing/TestCase.html#method_seed

使用这种方法,您只需要调用$this->seed('MySeederName');即可触发播种机。

因此,如果您希望此播种器在每次测试之前触发,您可以将以下 setUp 函数添加到您的测试类:

public function setUp()
{
    parent::setUp();
    $this->seed('MySeederName');
}

最终结果与以下相同:

 $this->artisan('db:seed',['--class' => 'MySeederName'])

或者

Artisan::call('db:seed', ['--class' => 'MySeederName'])

但是语法更简洁(在我看来)。

于 2018-12-12T22:09:25.360 回答
5

如果您正在使用RefreshDatabase测试特征:

abstract class TestCase extends BaseTestCase
{
    use CreatesApplication, RefreshDatabase {
        refreshDatabase as baseRefreshDatabase;
    }

    public function refreshDatabase()
    {
        $this->baseRefreshDatabase();

        // Seed the database on every database refresh.
        $this->artisan('db:seed');
    }
}
于 2019-02-14T20:49:20.603 回答
5

在 Laravel 8 中,RefreshDatabase 现在正在寻找一个名为“seed”的布尔属性。

    /** 
     * Illuminate\Foundation\Testing\RefreshDatabase
     * Determine if the seed task should be run when refreshing the database.
     *
     * @return bool
     */
    protected function shouldSeed()
    {
        return property_exists($this, 'seed') ? $this->seed : false;
    }

只需为您的测试类提供受保护的属性 $seed 并将其设置为 true 如果您想播种。

class ProjectControllerTest extends TestCase
{

    protected $seed = true;
    public function testCreateProject()
    {
        $project = Project::InRandomOrder()->first();
        $this->assertInstanceOf($project,Project::class);
    }

这种方法的好处是单个测试不会在每次运行时都播种。只有种子必要的测试才能建立数据库。

于 2021-03-09T16:07:25.553 回答
0

如果您更喜欢绕过 Artisan 的本地 DatabaseMigrations 和播种器/迁移方法,这是一个替代解决方案。您可以创建自己的特征来播种数据库:

namespace App\Traits;

use App\Models\User;
use App\Models\UserType;

trait DatabaseSetup 
{

    public function seedDatabase()
    {
        $user = $this->createUser();
    }

    public function createUser()
    {
        return factory(User::class)->create([
            'user_type_id' => function () {
                return factory(UserType::class)->create()->id;
            }
        ]);
    }

    public function getVar() {
        return 'My Data';
    }
}

然后在你的测试中这样调用它:

use App\Traits\DatabaseSetup;

class MyAwesomeTest extends TestCase
{
    use DatabaseSetup;
    use DatabaseTransactions;

    protected $reusableVar;

    public function setUp()
    {
        parent::setUp();
        $this->seedDatabase();
        $this->reusableVar = $this->getVar();
    }

    /**
     * @test
     */
    public function test_if_it_is_working()
    {
        $anotherUser = $this->createUser();
        $response = $this->get('/');
        $this->seeStatusCode(200);
    }

}
于 2018-03-09T22:47:41.843 回答