如何使 Mockery 过载选项在 Laravel 5 上正常工作
How to make Mockery overload option work properly on Laravel 5
我正在尝试在 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.
我做错了什么吗?有谁知道如何正确使用这个选项?
阅读页面,我认为这就是您要查找的错误:
When Mockery overloads a class, because of how PHP works with files,
that overloaded class file must not be included otherwise Mockery will
throw a “class already exists” exception. This is where autoloading
kicks in and makes our job a lot easier.
如果您从测试中删除这两行,将会导致您正在寻找的错误,它们被添加到手册页的第二个代码示例和第三个示例中的代码中:
* @runTestsInSeparateProcesses
* @preserveGlobalState disabled
这意味着您的代码不会利用 psr4 自动加载器或任何现有的自动加载器,并且会以牺牲速度为代价为您创建一个新的自动加载器,因为它不会使用您转储的 classmap 并且必须从头开始构建它。
如果你去掉上面的两行,你会得到预期的错误,因为它会尝试用同名的 class 使你的 class 过载。 class 已经自动加载,所以你会得到一个致命错误。
因此,如果您想阻止对 callBar
和 return void 的调用,这就是您的代码将要执行的操作,这是正确的。
删除 overload
将意味着您的模拟不再有效,因为您必须通过构造函数传递它才能使其工作。
更新:
根据你的更新,我可以得出结论,你的代码必须是 运行 模拟的 callBar
方法两次(不是实际的 callBar
方法),你的模拟 fooBar
class 使用 overload
。当模拟方法被调用时,真正的 callBar
方法内部什么都没有发生,它只是注册它被调用了。例如,如果您期望它一次,请编写 shouldReceive(1)
然后修复您的测试运行的代码。
当您删除 overload
时,全局注入不会发生,因此您的模拟永远不会被注入。但是,您在 mock class 上的 mock callBar
方法仍然期望 运行 两次,因此您会收到错误消息。您需要从测试中完全删除 2 行模拟代码。
保留 @ 语句,因为它有助于防止上述 psr4 错误。
这不是测试的方法,你不应该使用 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 !!!';
}
}
现在,假设你有一个独立的 class(没有控制器或类似的东西,你正在进行单元测试),你应该有这样的东西:
namespace App\Service;
use App\FooClass;
class ServiceForTesting
{
public function run(FooClass $class)
{
return $class->callBar();
}
}
我正在尝试在 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.
我做错了什么吗?有谁知道如何正确使用这个选项?
阅读页面,我认为这就是您要查找的错误:
When Mockery overloads a class, because of how PHP works with files, that overloaded class file must not be included otherwise Mockery will throw a “class already exists” exception. This is where autoloading kicks in and makes our job a lot easier.
如果您从测试中删除这两行,将会导致您正在寻找的错误,它们被添加到手册页的第二个代码示例和第三个示例中的代码中:
* @runTestsInSeparateProcesses
* @preserveGlobalState disabled
这意味着您的代码不会利用 psr4 自动加载器或任何现有的自动加载器,并且会以牺牲速度为代价为您创建一个新的自动加载器,因为它不会使用您转储的 classmap 并且必须从头开始构建它。
如果你去掉上面的两行,你会得到预期的错误,因为它会尝试用同名的 class 使你的 class 过载。 class 已经自动加载,所以你会得到一个致命错误。
因此,如果您想阻止对 callBar
和 return void 的调用,这就是您的代码将要执行的操作,这是正确的。
删除 overload
将意味着您的模拟不再有效,因为您必须通过构造函数传递它才能使其工作。
更新:
根据你的更新,我可以得出结论,你的代码必须是 运行 模拟的 callBar
方法两次(不是实际的 callBar
方法),你的模拟 fooBar
class 使用 overload
。当模拟方法被调用时,真正的 callBar
方法内部什么都没有发生,它只是注册它被调用了。例如,如果您期望它一次,请编写 shouldReceive(1)
然后修复您的测试运行的代码。
当您删除 overload
时,全局注入不会发生,因此您的模拟永远不会被注入。但是,您在 mock class 上的 mock callBar
方法仍然期望 运行 两次,因此您会收到错误消息。您需要从测试中完全删除 2 行模拟代码。
保留 @ 语句,因为它有助于防止上述 psr4 错误。
这不是测试的方法,你不应该使用 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 !!!';
}
}
现在,假设你有一个独立的 class(没有控制器或类似的东西,你正在进行单元测试),你应该有这样的东西:
namespace App\Service;
use App\FooClass;
class ServiceForTesting
{
public function run(FooClass $class)
{
return $class->callBar();
}
}