Laravel单元测试,如何"seeInDatabase"软删除的行?

12

我正在进行一个小单元测试,其中我软删除一行。为了将测试标记为成功,我需要使用以下条件找到该行:

  • 给定的ID和
  • deleted_at不应该为null。

我可以满足第一个条件——因为我知道这个ID。

不幸的是,我不知道如何告诉seeInDatabase方法我期望deleted_at不是null:

$this->seeInDatabase(
       'diary_note_categories',
       [
           'id' => 'a7e35ad0-6f00-4f88-b953-f498797042fc',
           'deleted_at' => null // should be is not null, like <> or != or whatever
       ]
 );

有什么提示吗?

'deleted_at <>' => null 出问题了

'deleted_at' => ['!=' => null] 同样也有问题


这个问题已经通过编写自定义方法seeIsSoftDeletedInDatabase得到解决。 - Matt Komarnicki
1
你能详细说明一下你是怎么解决的吗?你可以创建自己的答案。 - mniess
@mniess,请查看Espadav8的答案。我们一起工作,他提供了正确的解决方案。 - Matt Komarnicki
6个回答

38

我是这样做的:

$this->seeInDatabase('diary_note...',['id' => 'a7e35ad0'])
->notSeeInDatabase('diary_note...',['id' => 'a7e35ad0','deleted_at'=>null]);

所以我会分两步来检查:

  1. 我会检查表格中是否存在我们的ID记录
  2. 我会检查表格中是否不存在我们的ID记录且deleted_at为null

14
目前这是不可能的。无论是seeInDatabase还是notSeeInDatabase,它们都会直接将数组传递给查询构建器的where方法,而该方法在传递一个数组时无法处理除=以外的任何内容。

https://github.com/laravel/framework/blob/2b4b3e3084d3c467f8dfaf7ce5a6dc466068b47d/src/Illuminate/Database/Query/Builder.php#L452

public function where($column, $operator = null, $value = null, $boolean = 'and')
{
    // If the column is an array, we will assume it is an array of key-value pairs
    // and can add them each as a where clause. We will maintain the boolean we
    // received when the method was called and pass it into the nested where.
    if (is_array($column)) {
        return $this->whereNested(function ($query) use ($column) {
            foreach ($column as $key => $value) {
                $query->where($key, '=', $value);
            }
        }, $boolean);
    }

    // ...
}

选项1 - 将以下代码添加到您的TestCase类中,该类是您从中扩展测试用例的类

代码片段: https://gist.github.com/EspadaV8/73c9b311eee96b8e8a03

<?php
/**
 * Assert that a given where condition does not matches a soft deleted record
 *
 * @param  string $table
 * @param  array  $data
 * @param  string $connection
 * @return $this
 */
protected function seeIsNotSoftDeletedInDatabase($table, array $data, $connection = null)
{
    $database = $this->app->make('db');

    $connection = $connection ?: $database->getDefaultConnection();

    $count = $database->connection($connection)
        ->table($table)
        ->where($data)
        ->whereNull('deleted_at')
        ->count();

    $this->assertGreaterThan(0, $count, sprintf(
        'Found unexpected records in database table [%s] that matched attributes [%s].', $table, json_encode($data)
    ));

    return $this;
}

/**
 * Assert that a given where condition matches a soft deleted record
 *
 * @param  string $table
 * @param  array  $data
 * @param  string $connection
 * @return $this
 */
protected function seeIsSoftDeletedInDatabase($table, array $data, $connection = null)
{
    $database = $this->app->make('db');

    $connection = $connection ?: $database->getDefaultConnection();

    $count = $database->connection($connection)
        ->table($table)
        ->where($data)
        ->whereNotNull('deleted_at')
        ->count();

    $this->assertGreaterThan(0, $count, sprintf(
        'Found unexpected records in database table [%s] that matched attributes [%s].', $table, json_encode($data)
    ));

    return $this;
}

选项2 - 安装以下composer包

这个composer包与上面的代码完全相同,但被封装为Composer包。

composer require kirkbater/soft-deletes

然后在您的特定测试类中使用它:

<?php

use Kirkbater\Testing\SoftDeletes;

class MyTestClass extends TestClass {

    use SoftDeletes;

}

你已经将这个作为pull request添加了吗,@EspadaV8? - Nate Ritter
@NateRitter 没有,我认为它不会被接受。 - EspadaV8
实际上,检查存储库时有这个PR-https://github.com/laravel/framework/pull/11887-而Taylor不想合并它。 - EspadaV8
真遗憾 @EspadaV8。我向Taylor提出了一个问题,问为什么它不被包括在内,但是无论如何,现在我只是将其添加到我的测试用例中。这是一个很好的补充。感谢你这样做,即使Taylor认为不适合使用它。 - Nate Ritter

14

这是一个老问题,但对于那些使用较新版本的Laravel(5.4及以上),现在有一个assertSoftDeleted断言:文档

因此,对于原来的问题,答案现在应该是:

$this->assertSoftDeleted('diary_note_categories', [
    'id' => 'a7e35ad0-6f00-4f88-b953-f498797042fc'
]);

只是想指出这个不再起作用了。最新的 Laravel 9 在 assertSoftDeleted() 上使用模型实例,不能与数组一起使用。https://laravel.com/docs/9.x/database-testing#assert-deleted - Firze

6

确认给定的记录已被删除(适用于Laravel 5.4及以上版本)。

assertSoftDeleted(string|Model $table, array $data = [], string|null $connection = null)

使用id的示例:

$this->assertSoftDeleted('table_name', ['id'='value'])

使用模型的例子:

$user = User::factory()->create();
$user->delete();
$this->assertSoftDeleted($user);

2

我在使用 Laravel 6。

$this->assertDatabaseMissing('stores', [
'id' => $test_data['store']->id, 'deleted_at' => null
]);

$this->assertDatabaseHas('stores', ['id' => $id]);

-2

这个还没有经过测试,但可以尝试这样做:

$this->seeInDatabase(
       'diary_note_categories',
       [
           'id' => 'a7e35ad0-6f00-4f88-b953-f498797042fc',
           'deleted_at' => ['deleted_at' ,'!=', null ] // should be is not null, like <> or != or whatever
       ]
 );

1
你好,你是在猜测还是确定呢? [Illuminate\Database\QueryException] SQLSTATE[HY105]: 无效的参数类型:7 (SQL: select count(*) as aggregate from "diary_note_categories" where ("id" = a7e35ad0-6f00-4f88-b953-f498797042fc and "deleted_at" = deleted_at)) - Matt Komarnicki
我找不到这个函数的文档,但你可以在5分钟内测试它是否有效。因为这种方式被用于Eloquent查询。 - fico7489
我试过了,但不起作用。请查看Espadav8的答案以获取正确的解决方案。干杯。 - Matt Komarnicki

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接