如何在Lumen框架中使用Mockery进行模型Mock?恳请指导
我懂,Lumen官方文档确实有点精简,Mock Eloquent模型这块没讲太细,踩坑很正常。下面我就针对你的需求,一步步教你用Mockery和Facades两种方式,在updatePassword方法里完成两处UserModel的Mock操作。
前提准备
首先,确保你已经安装了Mockery:
composer require mockery/mockery --dev
如果要使用Facades方式,需要先在bootstrap/app.php里开启Facades支持,取消下面这行的注释:
$app->withFacades();
另外,先假设你的updatePassword方法大概是这样的(方便后续示例)——比如在一个UserService类中:
<?php namespace App\Services; use App\Models\UserModel; use Illuminate\Support\Facades\Hash; class UserService { public function updatePassword(int $userId, string $newPassword): bool { // 第一处:查找指定ID的用户 $user = UserModel::find($userId); if (!$user) { return false; } // 第二处:更新密码并保存 $user->password = Hash::make($newPassword); return $user->save(); } }
方法一:用Mockery直接Mock UserModel
这种方式不需要依赖Facades,直接Mock模型类的静态方法和实例方法,适合Lumen默认的轻量模式。
测试代码示例
<?php namespace Tests\Unit; use App\Models\UserModel; use App\Services\UserService; use Mockery; use Tests\TestCase; class UserServiceTest extends TestCase { // 测试后清理Mock,避免污染其他测试 public function tearDown(): void { Mockery::close(); parent::tearDown(); } // 测试密码更新成功的场景 public function test_update_password_success() { // 1. Mock UserModel的find方法,返回一个Mock的用户实例 $mockUser = Mockery::mock(UserModel::class); UserModel::shouldReceive('find') ->with(123) // 匹配传入的用户ID ->once() // 确保只被调用一次 ->andReturn($mockUser); // 2. Mock这个用户实例的save方法,返回true表示保存成功 $mockUser->shouldReceive('save') ->once() ->andReturn(true); // 执行测试并断言结果 $service = new UserService(); $result = $service->updatePassword(123, 'new_secure_password'); $this->assertTrue($result); } // 测试用户不存在的场景 public function test_update_password_user_not_found() { // Mock find方法返回null,表示用户不存在 UserModel::shouldReceive('find') ->with(456) ->once() ->andReturn(null); $service = new UserService(); $result = $service->updatePassword(456, 'new_password'); $this->assertFalse($result); } }
方法二:用Facades结合Mockery Mock UserModel
如果你的项目已经开启了Facades,这种方式和Laravel的Mock方式更接近,写法更统一。
测试代码示例
<?php namespace Tests\Unit; use App\Models\UserModel; use App\Services\UserService; use Mockery; use Tests\TestCase; class UserServiceFacadeTest extends TestCase { public function tearDown(): void { Mockery::close(); parent::tearDown(); } public function test_update_password_with_facades() { // 1. 用Facade的静态方法Mock find操作 $mockUser = Mockery::mock(UserModel::class); UserModel::shouldReceive('find') ->with(123) ->once() ->andReturn($mockUser); // 2. Mock用户实例的save操作 $mockUser->shouldReceive('save') ->once() ->andReturn(true); // 执行测试 $service = new UserService(); $result = $service->updatePassword(123, 'test_password'); $this->assertTrue($result); } }
额外场景:如果你的方法用了依赖注入
如果你的UserService是通过构造方法注入UserModel实例(而非静态调用),Mock方式会略有不同:
修改后的UserService
<?php namespace App\Services; use App\Models\UserModel; use Illuminate\Support\Facades\Hash; class UserService { protected $userModel; public function __construct(UserModel $userModel) { $this->userModel = $userModel; } public function updatePassword(int $userId, string $newPassword): bool { $user = $this->userModel->find($userId); if (!$user) { return false; } $user->password = Hash::make($newPassword); return $user->save(); } }
对应的测试代码
public function test_update_password_with_dependency_injection() { // Mock UserModel实例 $mockUserModel = Mockery::mock(UserModel::class); $mockUser = Mockery::mock(UserModel::class); // Mock find方法返回用户实例 $mockUserModel->shouldReceive('find') ->with(123) ->once() ->andReturn($mockUser); // Mock save方法 $mockUser->shouldReceive('save') ->once() ->andReturn(true); // 将Mock实例注入服务 $service = new UserService($mockUserModel); $result = $service->updatePassword(123, 'new_pass'); $this->assertTrue($result); }
关键注意事项
- 每次测试后一定要调用
Mockery::close(),清理所有Mock对象,防止测试之间的污染。 - 如果使用Lumen自带的
Tests\TestCase基类,它已经集成了Mockery的支持,不过手动清理更保险。 - Mock时要精准匹配参数(比如
with(123)),确保Mock的是你要的那处调用。
内容的提问来源于stack exchange,提问作者yang li
相关产品推荐
相关产品推荐

