You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

单元测试中Mock数据库查询返回空数组而非预期值的问题

单元测试Mock数据库查询失败问题修复

问题现象

在单元测试中Mock UserAccess 模型的数据库查询时,待测试函数里的dd($accessibleRules->all())始终返回空数组,而非预期的[1,2,3],尝试多种Mock方式均未解决。

待测试函数代码

public static function userCanFillCategories(User $user, array $article_ids)
{
    if(empty($article_ids)){
        return false;
    }

    // 原代码存在未定义变量$policy和$categoryIds,需修正
    $accessibleRules = UserAccess::where('user_id',$user->id)
        ->where('policy_id',$policy->id)
        ->whereIn('article_id',$article_ids)
        ->pluck('article_id');

    dd($accessibleRules->all());

    return empty(array_diff($categoryIds,$accessibleRules));
}

当前测试代码

namespace Tests\Feature\Model;

use App\Models\Access;
use App\Models\User;

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

class AccessTest extends TestCase
{
    public function testUserHasAccess()
    {
        $user = new User();
        $user->id = 1;

        $article_ids = [1,2,3];

        $mock = Mockery::mock(UserAccess::class);

        // 变量名错误:$categoryAccessMock未定义,实际应使用$mock
        $categoryAccessMock->shouldReceive('where')
            ->with('user_id', $user->id)
            ->shouldReceive('whereIn')
            ->with('article_id', $article_ids)
            ->andReturnSelf()->shouldReceive('pluck')
            ->with('article_id')
            ->andReturn($article_ids);

        $this->app->instance(UserAccess::class, $mock);

        $this->assertTrue(UserAccess::userCanFillCategories($user,$article_ids));
    }
}

尝试过的Mock方式

  • 方式一:
$categoryAccessMock->shouldReceive('where')
    ->with('user_id', $user->id)
    ->shouldReceive('where')
    ->with('category_id', $article_ids)
    ->andReturnSelf()->shouldReceive('pluck')
    ->with('article_id')
    ->andReturn(new Collection($article_ids));
  • 方式二:
$categoryAccessMock = Mockery::mock(CategoryAccess::class);
$categoryAccessMock->shouldReceive('where')
    ->with('user_id', $user->id)
    ->andReturnSelf();
$categoryAccessMock->shouldReceive('where')
    ->with('policy_id', $policy->id)
    ->andReturnSelf();
$categoryAccessMock
    ->shouldReceive('whereIn')
    ->with('category_id', $category_ids)
    ->andReturnSelf();
$categoryAccessMock
    ->shouldReceive('pluck')
    ->with('article_id')
    ->andReturn(new Collection($article_ids));
  • 方式三:
$mock->shouldReceive("where->whereIn->pluck")
    ->andReturn(new Collection($article_ids));

修复步骤

1. 修正测试代码变量名错误

测试中定义了$mock = Mockery::mock(UserAccess::class);,但后续误用未定义的$categoryAccessMock,导致Mock未生效,需统一变量名。

2. 精准匹配链式调用的所有方法及参数

待测试函数的查询是**两个where+一个whereIn+pluck**的链式调用,需确保每个方法的参数完全匹配,且每次调用都返回Mock实例本身(andReturnSelf())。

3. 修正待测试函数的未定义变量

原函数里的$policy和$categoryIds是未定义变量,需补充参数或修正逻辑,否则测试会直接报错。

修复后的测试代码

namespace Tests\Feature\Model;

use App\Models\User;
use App\Models\UserAccess;
use Illuminate\Database\Eloquent\Collection;
use Tests\TestCase;
use Mockery;

class AccessTest extends TestCase
{
    public function testUserHasAccess()
    {
        $user = new User();
        $user->id = 1;

        $article_ids = [1,2,3];
        // 补充policy变量,对应待测试函数中的$policy->id
        $policy = (object) ['id' => 1];
        // 补充categoryIds变量,对应返回逻辑的array_diff参数
        $categoryIds = [1,2,3];

        // 正确Mock UserAccess类
        $mock = Mockery::mock(UserAccess::class);

        // 链式匹配所有查询方法
        $mock->shouldReceive('where')
            ->once()
            ->with('user_id', $user->id)
            ->andReturnSelf();

        $mock->shouldReceive('where')
            ->once()
            ->with('policy_id', $policy->id)
            ->andReturnSelf();

        $mock->shouldReceive('whereIn')
            ->once()
            ->with('article_id', $article_ids)
            ->andReturnSelf();

        $mock->shouldReceive('pluck')
            ->once()
            ->with('article_id')
            ->andReturn(new Collection($article_ids));

        $this->app->instance(UserAccess::class, $mock);

        // 传入修正后所需的参数
        $this->assertTrue(UserAccess::userCanFillCategories($user, $article_ids, $policy, $categoryIds));
    }
}

修复后的待测试函数

use App\Models\User;
use App\Models\UserAccess;

public static function userCanFillCategories(User $user, array $article_ids, $policy, array $categoryIds)
{
    if(empty($article_ids)){
        return false;
    }

    $accessibleRules = UserAccess::where('user_id',$user->id)
        ->where('policy_id',$policy->id)
        ->whereIn('article_id',$article_ids)
        ->pluck('article_id');

    return empty(array_diff($categoryIds, $accessibleRules->all()));
}

关键注意点

  • Mock时必须确保变量名一致,否则会调用原类而非Mock实例。
  • 链式调用的每个方法都要单独Mock,参数必须与待测试代码完全匹配(包括顺序和值)。
  • pluck方法需返回Collection实例,原代码调用了$accessibleRules->all(),普通数组无法触发该方法。
  • 待测试函数的未定义变量必须修正,否则会导致测试报错,无法验证Mock效果。

内容的提问来源于stack exchange,提问作者Dimitrios Desyllas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 20:01:23