如何使 Mockery 重载选项在 Laravel 5 上正常工作
Posted
技术标签:
【中文标题】如何使 Mockery 重载选项在 Laravel 5 上正常工作【英文标题】:How to make Mockery overload option work properly on Laravel 5 【发布时间】:2021-08-22 09:09:32 【问题描述】:我正在尝试在 Laravel 5 上使用 Mockery 库的 overload
选项。
我现在的环境:
Laravel 5 嘲讽 1.0 phpUnit 7.5我写了这个测试用例:
namespace Tests\Unit;
use Mockery;
use Tests\TestCase;
/**
* @runTestsInSeparateProcesses
* @preserveGlobalState disabled
*/
class RenewSignatureTest extends TestCase
public function testHandle()
$mock = Mockery::mock('overload:App\FooClass');
$mock->shouldReceive('callBar')
->times(2);
根据documentation,这个测试应该失败,但不管我做什么,测试永远不会失败!它总是导致:
Time: 304 ms, Memory: 19.53 MB
OK (1 test, 1 assertion)
如果我删除 overload:
选项,测试将失败。所以我假设我没有按预期使用库的方法。
新测试:
namespace Tests\Unit;
use Mockery;
use Tests\TestCase;
/**
* @runTestsInSeparateProcesses
* @preserveGlobalState disabled
*/
class RenewSignatureTest extends TestCase
public function testHandle()
$mock = Mockery::mock('App\FooClass');
$mock->shouldReceive('callBar')
->times(2);
结果:
Mockery\Exception\InvalidCountException: Method callBar(<Any Arguments>) from Mockery_0__App_FooClass should be called exactly 2 times but called 0 times.
我做错了吗?有谁知道如何正确使用这个选项?
【问题讨论】:
【参考方案1】:阅读页面,我认为这是您正在寻找的错误:
当 Mockery 重载一个类时,由于 PHP 处理文件的方式, 不能包含重载的类文件,否则 Mockery 将 抛出“类已存在”异常。这是自动加载的地方 开始工作,让我们的工作轻松很多。
如果您从测试中删除这两行,则会导致您正在寻找的错误,它们被添加到第二个代码示例和手册页上的第三个示例中的代码中:
* @runTestsInSeparateProcesses
* @preserveGlobalState disabled
这意味着您的代码不会利用 psr4 自动加载器或任何现有的自动加载器,并且会以牺牲速度为代价为您创建一个新的自动加载器,因为它不会使用您转储的类映射并且必须构建它从头开始。
如果你去掉上面的两行,你会得到预期的错误,因为它会尝试用同名的类重载你的类。该类已经被自动加载,因此您会遇到致命错误。
因此,如果您想阻止对 callBar
的调用并返回 void,这就是您的代码将执行的操作,这是正确的。
删除overload
将意味着您的模拟不再有效,因为您必须将其传递给构造函数才能使其工作。
更新:
通过您的更新,我可以得出结论,您的代码必须运行两次模拟的 callBar
方法(不是实际的 callBar
方法),而您的模拟 fooBar
类使用 overload
。当被模拟的方法被调用时,真正的callBar
方法内部实际上并没有发生任何事情,它只是注册它被调用了。例如,如果您期望它一次,请编写 shouldReceive(1)
,然后修复您的测试运行的代码。
当您删除 overload
时,不会发生全局注入,因此您的模拟永远不会被注入。但是,您在模拟类上的模拟 callBar
方法仍期望运行两次,因此您会收到错误消息。您需要从测试中完全删除 2 个模拟代码行。
保留 @ 语句,因为它有助于防止上述 psr4 错误。
【讨论】:
好的。我不知道我理解正确,但是,我的问题是我在两种情况下拥有完全相同的类:1-如果放置重载选项,则测试通过(这不应该发生,因为我明确表示 fooBar 需要被调用 2 次) 2-如果我删除重载选项,测试失败,说 Mockery_0__App_FooClass 应该被调用两次。为什么会发生这种情况? PS:我知道我没有那么明确。对此我很抱歉。我更新了问题。 PS2:“为什么会发生这种情况”的意思是“为什么我在两个类中没有相同的结果?” PS3:请记住我正在使用 Laravel 的 TestCase。 Wich 意味着存在隐式方法,例如框架实现的 setUp 和 tearDown 。如果你愿意,我可以在这里发布这些方法。 PS4:即使我删除那些“@ cmets”并单独运行此测试,我也会得到相同的结果。【参考方案2】:这不是测试的方式,你永远不应该使用overload:
选项...... Laravel 可以帮助你做很多事情,不要再次创建***或尝试......
为什么需要使用@runTestsInSeparateProcesses
和@preserveGlobalState disabled
?
假设你的测试是这样的:
namespace Tests\Unit;
use App\FooClass;
use App\Service\ServiceForTesting;
use Mockery;
use Tests\TestCase;
class RenewSignatureTest extends TestCase
public function testHandle()
$mock = Mockery::mock(FooClass::class);
$mock->shouldReceive('callBar')
->times(2);
$myClass = app(ServiceForTesting::class);
$myClass->run($mock);
$myClass->run($mock);
你应该有这样的代码:
namespace App;
class FooClass
public function callBar()
return 'Works !!!';
现在,假设你有一个独立的类(没有控制器或类似的,你在一个单元测试中),你应该有这样的东西:
namespace App\Service;
use App\FooClass;
class ServiceForTesting
public function run(FooClass $class)
return $class->callBar();
【讨论】:
以上是关于如何使 Mockery 重载选项在 Laravel 5 上正常工作的主要内容,如果未能解决你的问题,请参考以下文章
Laravel - 如何使用 mockery 对更新用户数据的中间件进行单元测试
在 Laravel 4 中使用 Mockery 模拟自定义类