在 Laravel 6 中进行单元测试时处理模型关系 status/state 的最佳方式?

Best way to handle a model relationship status/state when unit testing in Laravel 6?

我在 Laravel 6.x 中使用 SQLite 进行了以下单元测试:

<?php

/** @var \Illuminate\Database\Eloquent\Factory $factory */

use App\Entry;
use App\EntryStatus;
use Faker\Generator as Faker;

$factory->define(Entry::class, function (Faker $faker) {
    return [
        'user_id' => 1,
        'caption' => $faker->sentence(10),
        'entry_status_id' => EntryStatus::where('name', 'pending')->first()->id,
    ];
});

$factory->state(Entry::class, 'awaiting_payment', [
    'entry_status' => EntryStatus::where('name', 'awaiting_payment')->first()->id,
]);

我有一个'Trying to get 属性 'id' of non-object'的测试错误,这是针对以下行:

'entry_status' => EntryStatus::where('name', 'awaiting_payment')->first()->id,

我对如何修复此错误有一些想法,但我想知道在单元测试和 'The Laravel way'.

方面的最佳实践是什么

按照我的想法,我有一个 EntryStatus table,它有静态状态 'pending' => 0,'awaiting_payment' => 1,'paid' = > 2 等等。我在我的 App\Entry 模型中创建了一个关系 > App\EntryStatus。思路如下:

  1. 原计划;对于单元测试,每次测试我都需要为静态 EntryStatus table 设置种子。查看文档,我会使用类似 setUp() > $this->artisan('db:seed') 的东西。但这感觉真的会减慢测试速度。除非有一种方法可以在所有测试开始之前对数据库进行一次播种。

  2. 尝试创建一个创建静态数据的工厂('pending' => 0,'awaiting_payment' => 1)但我需要手动更新数据库种子和工厂每次都匹配这看起来很笨重。

  3. 在 Laravel 文档测试文档中有这个例子; $factory->state(App\User::class, 'delinquent', ['account_status' => 'delinquent',]).这让我觉得我可以完全删除关系 table EntryStatus 并只使用 Entry 中的字符串列来表示状态。我认为这将是最好的解决方案,但我担心我们使用 id 作为状态是有原因的,因为我认为它们在搜索查询中更快。但如果没有,我的雕像将被修复,这似乎是最 eloquent 的解决方案。

另一种选择是无论如何都将雕像存储为整数,记下什么状态是什么整数,但这似乎也不正确。

记录尚未创建,在工厂中以这种方式分配静态关系不是一个好主意。

您可以使用工厂 create,然后在测试用例中分配关系。

例如:

Entry工厂

$factory->define(Entry::class, function (Faker $faker) {
    return [
        'user_id' => factory(User)->create()->id,
        'caption' => $faker->sentence(10),
        'entry_status_id' => factory(EntryStatus)->create()->id,
    ];
});

EntryStatus工厂

$factory->define(EntryStatus::class, function (Faker $faker) {
    return [
        'name' => $faker->word,
        'entry_status' => $faker->numberBetween(0, 2) //Its better start from 1 though
    ];
});

User工厂

$factory->define(User::class, function (Faker $faker) {
    return [
        'name' => $faker->word
    ];
});

在您的测试用例中开始将事物链接在一起(如果需要)。

/**
 * @test
 */
public function exampleTestCase()
{
    $enteryStatus = factory(EntryStatus::class)->create(['entry_status' => 1]);

    //create 6 entries
    $entry = factory(Entry::class, 6)->create(['entry_status_id' => $enteryStatus->id]);

    //TODO: assert something
}

你可以查看afterCreatingState

$factory
    ->state(EntryStatus::class, 'awaiting_payment', ['name' => 'awaiting_payment'])
    ->afterCreatingState(EntryStatus::class, 'awaiting_payment', function ($entryStatus, $faker) {
        factory(Entry::class)->create([
            'entry_status_id' => $entryStatus->id,
        ]);
    });