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

Laravel项目中使用PHPUnit+Mockery绕过第三方类方法抛出异常的测试问题

Laravel项目中使用PHPUnit+Mockery绕过第三方类方法抛出异常的测试问题

嗨,看了你的代码,我发现你现在的测试思路走偏啦!你当前是直接Mock了CompanyService自己的providePin方法,相当于完全跳过了这个方法的实际执行逻辑——但你真正想要的应该是:让CompanyService的providePin正常跑,只是绕过Mail::send抛出的异常,对吧?

问题出在你Mock错对象了:你需要Mock的是Mail这个静态类的send方法,而不是CompanyService的方法。下面给你修正后的测试代码,以及关键知识点解释:

正确的Mockery测试代码

use Mockery;
use PHPUnit\Framework\TestCase;
use App\Services\CompanyService; // 替换成你的实际命名空间
use App\Mail; // 替换成你的实际命名空间

class CompanyTest extends TestCase
{
    // 测试结束后清理Mockery,避免污染其他测试
    protected function tearDown(): void
    {
        parent::tearDown();
        Mockery::close();
    }

    // 测试邮件发送成功的场景
    public function testProvidePinWhenMailSucceeds()
    {
        // 用Mockery创建Mail静态类的别名Mock,拦截静态方法调用
        $mailMock = Mockery::mock('alias:' . Mail::class);
        $mailMock->shouldReceive('send')
            ->with(300386) // 匹配传入的pin参数
            ->times(1) // 断言这个方法被调用1次
            ->andReturnNull(); // 模拟方法正常执行,不抛出异常

        // 实例化真实的CompanyService(我们要测试它的逻辑,不能Mock它本身)
        $companyService = new CompanyService();
        $result = $companyService->providePin(300386);

        // 断言方法返回true,因为邮件发送成功
        $this->assertTrue($result);
    }

    // 测试邮件发送失败(抛出异常)的场景
    public function testProvidePinWhenMailFails()
    {
        // 模拟Mail::send抛出异常
        $mailMock = Mockery::mock('alias:' . Mail::class);
        $mailMock->shouldReceive('send')
            ->with(300386)
            ->times(1)
            ->andThrow(new \Exception('Send mail error'));

        $companyService = new CompanyService();
        $result = $companyService->providePin(300386);

        // 断言方法返回false,因为捕获到了异常
        $this->assertFalse($result);
    }
}

关键知识点说明

  • 为什么不Mock CompanyService?:我们的测试目标是验证CompanyService::providePin的try-catch逻辑是否正常工作,所以必须用真实的CompanyService实例,只Mock它依赖的外部服务(这里就是Mail类)。
  • Mockery::mock('alias:类名')的作用:这是Mockery专门用来处理静态类的语法,它会创建一个“别名类”来替代原有的静态类,这样所有对原静态类方法的调用都会被拦截到Mock对象上。
  • 必须清理Mock:在tearDown方法里调用Mockery::close(),可以确保每个测试结束后清理Mock对象,避免影响其他测试用例的执行。

额外小提示(Laravel专属)

如果你是在Laravel框架内测试,其实可以用框架自带的Mail::fake()功能,比Mockery更简洁:

use Illuminate\Support\Facades\Mail;

public function testProvidePinWhenMailSucceeds()
{
    // 启用Mail的fake模式,拦截所有邮件发送
    Mail::fake();
    
    // 断言Mail::send被正确调用
    Mail::shouldReceive('send')->with(300386)->once();

    $companyService = new CompanyService();
    $result = $companyService->providePin(300386);

    $this->assertTrue($result);
}

备注:内容来源于stack exchange,提问作者VolkswagenLov

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.17 08:18:06