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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 07:38:46