Laravel 10 单元测试没有看到来自 Artisan Command 的数据库更改

问题描述 投票:0回答:3

我有一个 artisan 命令 (Laravel 10) 可以更改数据库中的用户表。我可以确认命令本身的输出正在发生更改,看起来它确实在工作。

但是,当我运行单元测试时,它没有看到更新的数据库发生变化。

单元测试

public function test_promote_user_command_sets_user_as_super_admin()
{
    $user = $this->createUser('guest');

    $response = $this->artisan("user:promote {$user->email}")->assertSuccessful();

    $role = Role::where('name', '=', 'Super Admin')->first();
    $this->assertDatabaseHas('users', [
        'id' => $user->id,
        'role_id' => $role->id
    ]);
}

工匠命令

class PromoteUserCommand extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'user:promote {email}';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Promote a user to super admin.';

    /**
     * Execute the console command.
     */
    public function handle(): void
    {
        $role = Role::where('name', '=', 'Super Admin')->first();        
        $user = User::where('email', '=', $this->argument('email'))->first();

        $user->role_id = $role->id;
        $user->save();
    }
}

当我运行测试时,我得到这个输出:

  Failed asserting that a row in the table [users] matches the attributes {
    "id": 1,
    "role_id": 1
}.

Found similar results: [
    {
        "id": 1,
        "role_id": null
    }
].

但是,如果在 artisan 命令中我在

dd($user)
之后运行
$user->save()
然后我运行测试,我得到这个:

...
  #attributes: array:9 [
    "id" => 1
    "name" => "Karelle Schamberger"
    "email" => "[email protected]"
    "email_verified_at" => "2023-03-04 18:22:36"
    "password" => "$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi"
    "remember_token" => "Br992xsWw9"
    "created_at" => "2023-03-04 18:22:36"
    "updated_at" => "2023-03-04 18:22:36"
    "role_id" => 1
  ]
  #original: array:9 [
    "id" => 1
    "name" => "Karelle Schamberger"
    "email" => "[email protected]"
    "email_verified_at" => "2023-03-04 18:22:36"
    "password" => "$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi"
    "remember_token" => "Br992xsWw9"
    "created_at" => "2023-03-04 18:22:36"
    "updated_at" => "2023-03-04 18:22:36"
    "role_id" => 1
  ]
  #changes: array:1 [
    "role_id" => 1
  ]
...

所以我知道命令正在运行并且它正在正常工作。为什么在测试断言发生之前似乎已恢复此更改?


编辑添加:

我还更改了命令以在

$user->save()
之后对表进行数据库查找,并且数据库结果还显示更改在数据库中...

因此数据库更改发生在 artisan 命令中,但要么该更改被还原,要么单元测试无法看到最新的数据库状态。

我所有其他不涉及 artisan 命令的 94+ 测试都可以很好地用于数据库测试。只有工匠命令有这个问题。

php laravel testing tdd laravel-artisan
3个回答
1
投票

首先,你不用

user:promote whatever_value
,你用
artisan("user:promote", ['email' => $user->email])
.

第二,问题是

artisan
方法,返回一个类,然后调用
->assertSuccessful();
,最后一个方法不返回任何内容,但它正在使用
__destruct
,这意味着,当没有其他任何东西调用该类时,它将执行命令并返回类,然后调用
assertXXXXX
.

但是因为您将其存储在变量中,所以在变量超出范围之前不会执行任何操作,当测试完成时,因此命令不会运行...

这是

__destruct
代码,所以你可以看到它实际上是在执行
run()
,因此执行你的命令。

看到

$this->artisan(...)
返回一个
PendingCommand
,我之前分享的那个(
__destruct
)。

有关它的更多 PHP 官方信息这里。重要的部分是:

只要没有对特定对象的其他引用,或在关闭序列期间以任何顺序调用析构函数方法。


您的代码:

public function test_promote_user_command_sets_user_as_super_admin()
{
    $user = $this->createUser('guest');

    $response = $this->artisan("user:promote {$user->email}")->assertSuccessful();

    $role = Role::where('name', '=', 'Super Admin')->first();
    $this->assertDatabaseHas('users', [
        'id' => $user->id,
        'role_id' => $role->id
    ]);

    // Now the artisan command is executed
}

正确代码:

public function test_promote_user_command_sets_user_as_super_admin()
{
    $user = $this->createUser('guest');

    $this->artisan("user:promote {$user->email}")->assertSuccessful();

    // Now the artisan command is executed

    $role = Role::where('name', '=', 'Super Admin')->first();
    $this->assertDatabaseHas('users', [
        'id' => $user->id,
        'role_id' => $role->id
    ]);
}

1
投票

无法准确指出您的问题出在哪里,但有些事情并不符合预期。

你调用命令时用方括号括起来的值,我认为不应该有,因为你不会将它们包含在

cli
中。其次,第二个参数或
artisan()
可以是数据值。把这个改成。

$this->artisan("user:promote", ['email' => $user->email]);

其次,您实际上并不知道命令是否已崩溃。即使看起来只是很多小事情都会导致它崩溃并且您的测试只会继续,所以断言命令的结果将保证它被正确执行。

$this->artisan("user:promote", ['email' => $user->email])
    ->assertSuccessful();

0
投票

我发现了问题,但我不知道为什么这是个问题。

我不得不改变这一行:

$response = $this->artisan("user:promote {$user->email}")->assertSuccessful();

对此:

$this->artisan("user:promote {$user->email}")->assertSuccessful();

出于某种原因,试图将输出捕获到响应变量中会使这种行为变得奇怪。为了排除故障,我使用了 enableQueryLog。

\DB::enableQueryLog();
$response = $this->artisan("user:promote {$user->email}")->assertSuccessful();
dd(\DB::getQueryLog());

这表明没有任何改变(即使我在命令中执行了 dd 它仍然有效......),当我删除

$response = 
然后查询返回预期结果。

© www.soinside.com 2019 - 2024. All rights reserved.