我正在为我的laravel 5应用程序编写单元测试。在一个测试中,我调用了一个函数,并且需要验证是否在被测函数中调用了另一个模型的静态create
方法。
我不想在数据库中持久化任何资源。
我已经创建了一个模拟对象,指定了期望,并将其绑定到应用程序实例,但是当调用create
时,应用程序试图运行SQL,而不是让模拟对象拦截函数调用。
public function testLogCreation() {
$this->log = Mockery::mock('App'Models'Log');
$this->log->shouldReceive('create')->once();
$this->app->instance('App'Models'Log',$this->log);
echo get_class($this->app['App'Models'Log']); // output: Mockery_2_App_Models_Log
}
有1个错误
SQLSTATE[23503]: Foreign key violation: 7 ERROR: insert or update on table "logs" violation Foreign key constraint....
我也尝试了$this->log->shouldReceive('save')->once();
,因为我观察到静态create
函数调用公共save
函数,但我认为我没有在log
的正确实例上创建模拟期望。
如果这不能完成,有什么替代策略的建议吗?
谢谢!
静态方法在单元测试中是出了名的难以处理。行
$this->app->instance('App'Models'Log', $this->log);
在应用程序中安装mock用于依赖注入,但是依赖注入只对由Laravel创建的对象起作用。静态方法实际上没有有一个底层对象,所以它不适用。
你可以使用的一种方法是拥有createLog
方法的工厂或服务接口。然后,这个接口有一个具体的类,它使用静态方法(或者更好的是,消除了对静态方法的需要)创建Log模型。然后,您可以轻松地在测试中模拟该接口,并验证是否调用了createLog
。
看这里一个很好的回答类似的问题:Laravel依赖注入:什么时候你必须这样做?什么时候可以嘲笑正面?两种方法的优点?
您可以通过通过服务容器实例化Eloquent模型并在生产代码中对其运行save()(而不是使用create()方法)来解决这个问题。
$log = $this->app->make('App'Models'Log')->fill($array);
$log->save();
这允许您模拟实际对象,而不是facade或静态方法。然后你可以像在你的例子中一样使用mock,只模拟'fill'和'save'方法。