由于集成了Mockery,Laravel的Facade对单元测试非常友好,只需要一行代码就能实现mocking。

例如,测试目标方法中调用了订单工具类的一个方法,在使用依赖注入的方式时,需要用三行代码实现对这个方法的mocking:

1
2
3
$fakeOrderTool = m::mock(\App\Tool\Order::class);
$fakeOrderTool->shouldReceive('getById')->once()->with($id)->andReturn($fakeOrder);
$this->app->instance(\App\Tool\Order::class, $fakeOrderTool);

而用Facade是这样的:

1
OrderFacade::shouldReceive('getById')->once()->with($id)->andReturn($fakeOrder);

当测试目标方法也属于订单工具类时,需要部分mock这个类,所以还要加上一行:

1
OrderFacade::makePartial();

但如果订单工具类的构造方法里有需要注入的依赖关系,这里并不会被执行。所以我在工具类基类里封装了一个方法,用反射机制实现手工注入依赖关系:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* 注入依赖,用于单元测试
*
* @return void
* @author donieli
* @since 5
* @version 5
*/
public function injectDependencies()
{
$reflector = new \ReflectionClass(get_class($this));
$params = $reflector->getConstructor()->getParameters();
$args = [];
foreach ($params as $param) {
$paramClass = $param->getClass();
if (empty($paramClass)) {
throw new \Exception('Dependency named '.$param->getName().' cannot be injected.');
}
$instance = app()->make($paramClass->getName());
$args[] = $instance;
}
call_user_func_array([$this, '__construct'], $args);
}

然后就可以这样处理:

1
2
3
OrderFacade::shouldReceive('getById')->with($id)->once()->andReturn($fakeOrder);
OrderFacade::makePartial();
OrderFacade::injectDependencies();