A/B测试如何驱动真实营收增长:从业务漏斗到财务验证
2026/6/6 10:26:11
Facade::shouldReceive()是 Laravel 测试中Mock 门面(Facade) 的核心方法,其底层并未修改服务容器(Service Container),而是动态替换门面的 accessor(访问器),将调用重定向到 Mockery 对象。
Cache::get())通过getFacadeAccessor()获取服务名(如'cache')// Illuminate\Support\Facades\FacadeprotectedstaticfunctionresolveFacadeInstance($name){returnstatic::$resolvedInstance[$name]??app($name);}shouldReceive()的关键操作Mockery::mock()创建 Mock 对象Facade::$resolvedInstance// Illuminate\Support\Facades\FacadepublicstaticfunctionshouldReceive(){$mock=Mockery::mock(...);static::$resolvedInstance[static::getFacadeAccessor()]=$mock;return$mock;}✅本质:
shouldReceive()是门面层的 Mock,非容器层的绑定替换。
// 测试中Cache::shouldReceive('get')->andReturn('mocked');// 服务容器仍返回真实实例$realCache=app('cache');// 不是 Mockery 对象!Facade::$resolvedInstance是门面内部的静态缓存,与容器app()->make()无关graph LR A[Cache::get('key')] --> B{Facade::$resolvedInstance['cache'] exists?} B -->|Yes| C[Call Mockery Object] B -->|No| D[app('cache') → Real Instance]// 测试支付流程publicfunctiontest_payment_success(){// Mock 支付网关门面PaymentGateway::shouldReceive('charge')->once()->with(100,'user_token')->andReturn(true);$result=$this->post('/checkout',['amount'=>100]);$this->assertTrue($result['success']);}// Mock 邮件发送Mail::shouldReceive('to')->andReturnSelf();Mail::shouldReceive('send')->once();User::register('test@example.com');// 不会真发邮件public function __construct(Cache $cache))app(Cache::class))$this->mock()(Laravel 8+):$mock=$this->mock(Cache::class,function($mock){$mock->shouldReceive('get')->andReturn('mocked');});Cache::shouldReceive('get');// 只影响 Cache 门面DB::shouldReceive('table');// 不影响 DB 门面CreatesApplicationtrait 在tearDown中重置Facade::$resolvedInstanceprotectedfunctiontearDown():void{Facade::clearResolvedInstances();parent::tearDown();}$this->mock()的对比(Laravel 8+)| 方法 | 作用层 | 适用对象 | 底层机制 |
|---|---|---|---|
Facade::shouldReceive() | 门面层 | 门面(Cache/Mail) | 替换Facade::$resolvedInstance |
$this->mock() | 容器层 | 任何类/接口 | 绑定 Mockery 对象到容器 |
// 门面 Mock(仅门面调用生效)Cache::shouldReceive('get')->andReturn('mocked');// 容器 Mock(所有解析方式生效)$this->mock(Cache::class,function($mock){$mock->shouldReceive('get')->andReturn('mocked');});✅原则:
- 用门面? →
shouldReceive()- 用依赖注入? →
$this->mock()
Facade::shouldReceive()// Illuminate\Support\Facades\FacadepublicstaticfunctionshouldReceive(){$name=static::getFacadeAccessor();// 1. 创建 Mockery Mock$mock=Mockery::mock(...func_get_args());// 2. 存入门面静态缓存(绕过容器)static::$resolvedInstance[$name]=$mock;return$mock;}Facade::clearResolvedInstances()// 测试后清理publicstaticfunctionclearResolvedInstances(){static::$resolvedInstance=[];}| 问题 | 答案 |
|---|---|
shouldReceive()修改容器绑定吗? | ❌否,仅替换门面内部缓存 |
| 底层机制? | ✅Facade::$resolvedInstance存储 Mockery 对象 |
| 适用场景? | ✅Mock 门面调用(如Cache/Mail) |
| 如何 Mock 依赖注入? | ✅用$this->mock()(Laravel 8+) |
核心原则:
门面 Mock 是“快捷方式”,容器 Mock 是“根本解法”。
理解二者差异,
才能写出精准、可靠的 Laravel 测试。