如何在Laravel应用中Mock Stripe以测试退款功能?
解决Stripe-PHP静态方法Mock问题的方案
推荐方案:重构代码,依赖注入StripeClient
直接使用静态方法会导致代码耦合度高,难以测试。推荐改用Stripe官方提供的StripeClient实例化方式,通过依赖注入实现可测试性。
1. 修改StripeRefundStrategy类
use Stripe\StripeClient; use Stripe\Refund; class StripeRefundStrategy implements RefundStrategyInterface { private StripeClient $stripe; // 注入StripeClient实例,而非直接调用静态方法 public function __construct(StripeClient $stripe) { $this->stripe = $stripe; } public function customer() { // TODO: Implement customer() method. } public function refund(string $chargeId) { // 使用实例化的客户端调用退款接口,传入实际的charge ID return $this->stripe->refunds->create([ 'charge' => $chargeId, 'amount' => 500, 'currency' => 'usd' ]); } }
2. 在Laravel服务提供者中绑定StripeClient
在AppServiceProvider的register方法中添加绑定,确保项目运行时能自动注入正确的客户端实例:
use Stripe\StripeClient; public function register() { $this->app->bind(StripeClient::class, function () { return new StripeClient(config('stripe.SecretKey')); }); }
3. 编写测试代码
通过Mock StripeClient及其内部的refunds客户端来模拟退款行为:
use Stripe\StripeClient; use Stripe\Refund; beforeEach(function () { // Mock退款客户端的create方法 $mockRefunds = Mockery::mock(); $mockRefunds->shouldReceive('create') ->with([ 'charge' => 'ch_3MmlLrLkdIwHu7ix0snN0B15', 'amount' => 500, 'currency' => 'usd' ]) ->once() ->andReturn(new Refund(['id' => 'test_refund_123'])); // MockStripeClient,使其返回上述退款客户端 $mockStripe = Mockery::mock(StripeClient::class); $mockStripe->shouldReceive('refunds')->andReturn($mockRefunds); // 将Mock实例绑定到容器 $this->app->instance(StripeClient::class, $mockStripe); $this->refundStrategy = $this->app->make(StripeRefundStrategy::class); }); it('can refund product', function () { asUser()->assertAuthenticated(); $refund = $this->refundStrategy->refund('ch_3MmlLrLkdIwHu7ix0snN0B15'); // 断言退款结果符合预期 expect($refund->id)->toBe('test_refund_123'); })->group('paymentRefunds');
临时方案:Mock静态方法(不推荐)
如果无法重构代码,可使用Mockery的overload:语法模拟静态类的方法,但这种方式耦合性高,不利于长期维护。
测试代码示例
beforeEach(function () { // 重载Stripe\Refund类,Mock其静态create方法 Mockery::mock('overload:Stripe\Refund') ->shouldReceive('create') ->with([ 'id' => 'test', 'amount' => 500, 'currency' => 'usd' ]) ->once() ->andReturn((object)['id' => 'test_refund']); // 模拟配置项,避免构造函数读取真实密钥 config()->set('stripe.SecretKey', 'TestKey'); $this->refundStrategy = $this->app->make(StripeRefundStrategy::class); }); it('can refund product', function () { asUser()->assertAuthenticated(); $refund = $this->refundStrategy->refund('ch_3MmlLrLkdIwHu7ix0snN0B15'); expect($refund->id)->toBe('test_refund'); })->group('paymentRefunds');
原测试失败原因说明
- 原测试Mock的是
Stripe类,但实际退款调用的是Refund::create静态方法,两者无关,导致Mock无效。 - 静态方法无法通过容器绑定实例的方式Mock,必须使用
overload:语法模拟类本身。 - 原
refund方法中硬编码了id => 'test',未使用传入的$id参数,属于业务逻辑错误,需修正。
内容的提问来源于stack exchange,提问作者Kristian Vasilev
相关产品推荐
相关产品推荐

