如何在laravel 5中模拟模型上的create方法?


How can I mock the create method on a model in laravel 5?

我正在为我的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'方法。