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

时间:2015-09-17 21:13:00

标签: php unit-testing laravel eloquent mockery

我正在为laravel 5应用程序编写单元测试。在一个测试中,我调用一个函数,需要验证它是否在被测函数中调用另一个模型的静态create方法。

我不想将任何资源实际保存到数据库中。

我创建了一个模拟对象,指定了期望,并将其绑定到app实例,但是当调用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]:外键冲突:7错误:在表格上插入或更新"记录"违反外键约束......

我也尝试了$this->log->shouldReceive('save')->once();,因为我发现静态create函数调用了公共save函数,但我认为我没有在正确的实例上创建模拟期望log

如果无法完成,还有其他策略的建议吗?

谢谢!

2 个答案:

答案 0 :(得分:1)

众所周知,静态方法难以应对单元测试。这条线

$this->app->instance('App\Models\Log', $this->log);

在依赖注入的应用程序中安装mock,但依赖注入仅与Laravel创建的对象一起发挥作用。静态方法实际上没有底层对象,所以它不适用。

您可以使用的一种方法是具有createLog方法的工厂或服务接口。这个接口有一个具体的类,它使用静态方法创建Log模型(或者更好的是,不需要静态方法)。然后,您可以在测试中轻松模拟该接口,并验证是否已调用createLog

请点击此处查看类似问题的绝佳答案:Laravel Dependency Injection: When do you have to? When can you mock Facades? Advantages of either method?

答案 1 :(得分:0)

您可以通过服务容器实例化Eloquent模型并在生产代码中运行save()(而不是使用create()方法)来解决问题。

$log = $this->app->make('App\Models\Log')->fill($array);
$log->save();

这允许您模拟实际对象而不是外观或静态方法。然后你可以像你的例子一样使用Mockery,只是模仿'填充'并且'保存'方法。