如何使用内存数据库中的 sqlite 在 laravel 5.5 中运行单元测试

Posted

技术标签:

【中文标题】如何使用内存数据库中的 sqlite 在 laravel 5.5 中运行单元测试【英文标题】:how to run unittests in laravel 5.5 using a sqlite in memory database 【发布时间】:2018-07-29 12:56:37 【问题描述】:

我设置了一个测试数据库:

phpunit.xml:

<phpunit>
    <!... other stuff ...>
    <php>
        <env name="APP_ENV" value="testing"/>
        <env name="CACHE_DRIVER" value="array"/>
        <env name="SESSION_DRIVER" value="array"/>
        <env name="QUEUE_DRIVER" value="sync"/>
        <env name="DB_CONNECTION" value="sqlite_testing"/>
    </php>
</phpunit>

database.php:

'connections' => [
    'sqlite_testing' => [
        'driver' => 'sqlite',
        'database' => ':memory:',
        'prefix' => '',
    ],

DatabaseSeeder.php

<?php

use Illuminate\Database\Seeder;

class DatabaseSeeder extends Seeder

    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    
        $this->call([
            MyTableSeeder::class
        ]);
    

MyTableSeeder.php:

<?php

use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
use App\My\Model;

class MyTableSeeder extends Seeder

    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    
        DB::table('model')->insert([
            'id' => 1,
            'created_at' => \Carbon\Carbon::now(),
            'updated_at' => \Carbon\Carbon::now()
        ]);

        /**
         * create random data
         */
        factory(Model::class, 50)->create();
    

ModelFactory.php:

<?php

use Faker\Generator as Faker;

$factory->define(\App\My\Model::class, function (Faker $faker) 
    return [
        'id' => $faker->unique()->randomNumber(),
        'created_at' => $faker->dateTime('now'),
        'updated_at' => $faker->dateTime('now')
    ];
);

MyTest.php:

<?php

namespace Tests\Unit;

use App\My\Model;
use Tests\TestCase;
use Illuminate\Foundation\Testing\RefreshDatabase;

class MyTest extends TestCase

    use RefreshDatabase;

    public function testGettingModel() 
        var_dump(Model::all()); // <-- returns a collection without any items

        $model = Model::where('id', 1)->first();

        $this->assertEquals('1', $model->id); // <-- trying to get property of non-object
    

所以在测试运行时,似乎数据库是由特征迁移但没有播种,因此没有返回任何内容。 但是文档没有说明(或者我找不到)如何在测试时为数据库播种。 它说明了如何通过运行“php artisan db:seed”手动播种,但这显然在测试中不起作用,因为测试后数据库不再存在。而且我无法手动运行它,因为在测试之前数据库不存在。这也会使测试变得不切实际。

一些示例说明在测试的 setup 方法中测试之前运行播种,如下所示:

public function setUp() 
    Artisan::call('db:seed');

但是包含这个语句会导致错误:

RuntimeException:尚未设置外观根。 (在/vendor/laravel/framework/src/Illuminate/Support/Facades/Facade.php:218)

或者像这样运行它:

public function setUp()

    $this->artisan('db:seed');

导致:

错误:在 null 上调用成员函数 call()(在 /vendor/laravel/framework/src/Illuminate/Foundation/Testing/Concerns/InteractsWithConsole.php:18)

我该怎么做呢?任何地方都有完整的工作示例吗?到目前为止我找不到任何:(

【问题讨论】:

我用github.com/orchestral/testbench-core做我的测试,它基本上简化了事情,但是它是用来做包测试的,不知道能帮上多少忙 感谢您的建议,但我想用框架本身来做。 【参考方案1】:

您必须在 setUp 方法中调用$this-&gt;seed();。但是你需要确保之前调用了父 setUp 方法,否则会失败。

<?php

namespace Tests\Unit;

use App\My\Model;
use Tests\TestCase;
use Illuminate\Foundation\Testing\RefreshDatabase;

class MyTest extends TestCase

    use RefreshDatabase;

    public function setUp()
    
        parent::setUp();
        $this->seed();
    

    public function testGettingModel() 
        $model = Model::where('id', 1)->first();
        $this->assertEquals('1', $model->id); // will assert to true if the id actually exists
    

【讨论】:

以上是关于如何使用内存数据库中的 sqlite 在 laravel 5.5 中运行单元测试的主要内容,如果未能解决你的问题,请参考以下文章

AnyDac - 如何断开与内存中的 sqlite db 的连接?

在 Laravel PHPUnit 测试中使用内存中的 SQLite

SQLite 如何变成 内存数据库

如何将 CoreData SQLite 支持的持久存储克隆到“内存中”?

SQLite——临时文件 & 内存数据库(待续)

SQLite——临时文件 & 内存数据库(待续)