单元测试中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
相关产品推荐
相关产品推荐

