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

如何用PHPUnit 10 Mock Laravel Cashier的Billable trait checkout方法?

问题分析与解决

你的Mock失败核心原因是没理清Cashier链式调用的对象关系,以及Mock时的参数匹配、对象实例指向错误:

错误点拆解

  1. 直接Mock User类的链式方法无效
    newSubscription、trialUntil、allowPromotionCodes这些方法并非User类本身的方法:newSubscription是Billable trait提供的,调用后返回SubscriptionBuilder实例,后续的链式方法都是SubscriptionBuilder的方法,所以直接在User的partial mock上期望这些链式调用完全不生效。

  2. Mock SubscriptionBuilder时的参数匹配问题
    你之前的写法里存在几个潜在问题:

    • 参数名不匹配:代码中newSubscription的参数是$name和$price,但Mock时写的是with($product, $price),变量名对应错误会导致Mock不触发。
    • 未匹配trialUntil和checkout的参数:如果Mock时没有指定参数(或参数不匹配),链式调用会因为方法调用不匹配而中断。
    • 可能未正确引入SubscriptionBuilder的命名空间:必须确保Mock的是Laravel\Cashier\SubscriptionBuilder类。

正确Mock写法

以下是能正确覆盖你业务逻辑的Mock代码:

use Laravel\Cashier\SubscriptionBuilder;
use Laravel\Cashier\Checkout;
use Carbon\Carbon;

// 1. Mock Checkout实例,返回预设的结账URL
$checkoutMock = Mockery::mock(Checkout::class);
$checkoutMock->url = 'https://chicken.com';

// 2. Mock SubscriptionBuilder,处理链式调用
$subscriptionBuilderMock = Mockery::mock(SubscriptionBuilder::class);

// 匹配trialUntil的参数(用type匹配Carbon实例,避免严格时间匹配)
$subscriptionBuilderMock->shouldReceive('trialUntil')
    ->with(Mockery::type(Carbon::class))
    ->andReturnSelf();

// allowPromotionCodes无参数,直接返回自身
$subscriptionBuilderMock->shouldReceive('allowPromotionCodes')
    ->andReturnSelf();

// 匹配checkout的参数(如果需要严格匹配数组,可传入完整数组;用any()则忽略参数细节)
$subscriptionBuilderMock->shouldReceive('checkout')
    ->with(Mockery::any())
    ->andReturn($checkoutMock);

// 3. Mock User类,指定newSubscription返回Mock的SubscriptionBuilder
$userMock = Mockery::mock(User::class)->makePartial();
// 注意这里的参数要和你业务代码中传入的$name、$price一致,用any()可忽略参数
$userMock->shouldReceive('newSubscription')
    ->with(Mockery::any(), Mockery::any())
    ->andReturn($subscriptionBuilderMock);

额外注意事项

  • 如果需要严格匹配checkout的参数数组,可以把Mockery::any()替换为实际的参数数组,比如:
    ->with([
        'success_url' => $expectedSuccessUrl,
        'cancel_url' => $expectedCancelUrl,
        'consent_collection' => ['terms_of_service' => 'required'],
    ])
    
  • 确保测试类引入了Mockery的PHPUnit集成,比如在测试类顶部添加:
    use Mockery\Adapter\Phpunit\MockeryPHPUnitIntegration;
    
  • 测试结束后要清理Mockery,可在tearDown方法中添加:
    protected function tearDown(): void
    {
        parent::tearDown();
        Mockery::close();
    }
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 23:15:39