如何模拟 Laravel Eloquent 模型的静态方法?

Posted

技术标签:

【中文标题】如何模拟 Laravel Eloquent 模型的静态方法?【英文标题】:How to mock static methods of a Laravel Eloquent model? 【发布时间】:2016-09-24 04:50:50 【问题描述】:

我的代码中有这样一行:

ModelName::create($data);

其中 ModelName 只是一个 Eloquent 模型。有没有办法在单元测试中模拟这个调用?我试过了:

$client_mock = \Mockery::mock('Eloquent','App\Models\ModelName');
$client_mock->shouldReceive('create')
            ->with($data)->andReturns($returnValue);

但它不起作用。

【问题讨论】:

是错字吗? ->andReturn(...); 【参考方案1】:

你应该这样做:

$client_mock = \Mockery::mock('overload:App\Models\ModelName');
$client_mock->shouldReceive('create')->with($data)->andReturn($returnValue);

我们使用overload: 是因为您不想将模拟传递给某个类,但您也想使用它以防它被硬编码到某些类中。

除了你的测试类(就在class之前)你应该添加:

/**
 * @runTestsInSeparateProcesses
 * @preserveGlobalState disabled
 */

为了避免这个类已经加载的错误(它可能在单个测试中没有它,但是当你运行多个测试时它可能不会)。

您可以阅读Mocking hard dependencies 了解有关它的详细信息。

更新

在某些情况下,使用此方法可能无法模拟类。在这些情况下,您可以创建一个普通的模拟(不带overload)并将其注入服务容器,如下所示:

App::instance('\App\Models\ModelName', $client_mock); 

【讨论】:

嗯,我试过你的方法,但这是我得到的 Mockery\Exception\RuntimeException: Could not load mock App\Models\ModelName, class already exists 。我还添加了@runTestsInSeparateProcesses 和@preserveGlobalState。 @Zed 你应该确保它是你测试的第一行。如果您已经使用 ModelName 进行了其他操作 - 直接在方法中或在其他地方(例如 setUp 方法)它将不起作用,因为它将首先创建真实对象并且下次无法重载它。 在模拟公共静态方法调用时,您应该使用alias:App\Models\ModelName 而不是overload:\Models\ModelNameoverload 用于拦截和模拟类实例化(例如 new class()),alias 用于公共静态方法(Class::method)。 @Zed 如果你还有这个问题,你可以看看我更新的答案 /** * @runInSeparateProcess * @preserveGlobalState disabled */

以上是关于如何模拟 Laravel Eloquent 模型的静态方法?的主要内容,如果未能解决你的问题,请参考以下文章

Laravel 5 - 使用 Mockery 模拟 Eloquent 模型

如何用 phpunit 模拟 Laravel Eloquent 模型?

使用 Mockery Eloquent 模型模拟 Laravel

在 Laravel(流明)上使用 Mockery 模拟 Eloquent 模型不起作用

模拟静态 Eloquent 模型方法,包括 find()

Laravel 4 在控制器中注入 Eloquent 模型