Laravel 5.6 单元测试在 null 上调用成员函数 beginTransaction()

jre*_*enk 3 php phpunit mongodb laravel laravel-5.6

我有一个运行 5.6 版的 Laravel 项目。连接的数据库是带有 jenssegers/mongodb 包的 mongodb。

我编写了一个单元测试来测试与用户相关的功能。

该测试在配置的测试 mongodb 数据库中创建新用户。我想在每次测试运行后刷新数据库,所以我使用了RefreshDatabaseTrait。

使用RefreshDatabaseTrait 时,我在运行测试时收到以下错误:

有 1 个错误:

1) Tests\Unit\UserTest::it_gets_top_user 错误:在 null 上调用成员函数 beginTransaction()

当不使用 Trait 时,测试会在数据库中创建所有必要的内容并执行断言而不会出错。

测试看起来像这样:

/** @test */
public function it_gets_top_user()
{
    factory(\App\Users\User::class, 5)->create();

    $userOne = factory(\App\Users\User::class)->create([
        'growth' => 10
    ]);

    $topUser = Users::getTopUser();

    $collection = new Collection();

    $collection->push($userOne);


    $this->assertEquals($collection, $topUser);
} 
Run Code Online (Sandbox Code Playgroud)

我在我的 composer.json 中使用以下版本:

"laravel/framework": "5.6.*",
"jenssegers/mongodb": "3.4.*",
"phpunit/phpunit": "~7.0",
Run Code Online (Sandbox Code Playgroud)

服务器上使用了以下版本:

  • PHP 7.2
  • MongoDB 3.4
  • PHP MongoDB 扩展 1.4.2

我使用安装在供应商目录中的 phpunit 调用测试:

vendor/phpunit/phpunit/phpunit
Run Code Online (Sandbox Code Playgroud)

jre*_*enk 5

问题似乎是,RefreshDatabaseTrait 在 MongoDB 环境中根本不起作用。

我通过在我的 Laravel 项目目录中创建我自己的RefreshDatabaseTrait解决了上述问题testing/。

Trait 看起来像这样:

<?php

namespace Tests;

trait RefreshDatabase
{
    /**
     * Define hooks to migrate the database before and after each test.
     *
     * @return void
     */
    public function refreshDatabase()
    {
        $this->dropAllCollections();
    }

    /**
     * Drop all collections of the testing database.
     *
     * @return void
     */
    public function dropAllCollections()
    {
        $database = $this->app->make('db');

        $this->beforeApplicationDestroyed(function () use ($database) {
            // list all collections here
            $database->dropCollection('users');
        });
    }
}
Run Code Online (Sandbox Code Playgroud)

为了启用这个特性,我覆盖setUpTraits了TestCase类中的函数。现在看起来像这样:

/**
 * Boot the testing helper traits.
 *
 * @return array
 */
protected function setUpTraits()
{
    $uses = array_flip(class_uses_recursive(static::class));

    if (isset($uses[\Tests\RefreshDatabase::class])) {
        $this->refreshDatabase();
    }

    if (isset($uses[DatabaseMigrations::class])) {
        $this->runDatabaseMigrations();
    }

    if (isset($uses[DatabaseTransactions::class])) {
        $this->beginDatabaseTransaction();
    }

    if (isset($uses[WithoutMiddleware::class])) {
        $this->disableMiddlewareForAllTests();
    }

    if (isset($uses[WithoutEvents::class])) {
        $this->disableEventsForAllTests();
    }

    if (isset($uses[WithFaker::class])) {
        $this->setUpFaker();
    }

    return $uses;
}
Run Code Online (Sandbox Code Playgroud)

最后,在我所有的测试类中,我可以像这样使用我新创建的 Trait:

<?php

namespace Tests\Unit;

use Illuminate\Database\Eloquent\Collection;
use Tests\RefreshDatabase;
use Tests\TestCase;

class UserTest extends TestCase
{
    use RefreshDatabase;

    // tests happen here
}
Run Code Online (Sandbox Code Playgroud)

  • 谢谢你的回答。一个改进是通过简单地调用 `$uses = parent::setUpTraits();` 来避免复制/粘贴 Laravel 的 `setUpTraits()` 方法,然后只添加检查自定义特征的 `if`。同样为了避免手动“列出所有集合”,您可以迭代 `foreach ($database-&gt;listCollections() as $coll)`,然后调用 `$database-&gt;dropCollection($coll-&gt;getName());`。 (2认同)