laravel Mock 应该被精确调用 1 次但被调用 0 次
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/33070381/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Mock should be called exactly 1 times but called 0 times
提问by Grzegorz Gajda
I have weird problem with Laravel 5 and PHPUnit. When I try to mock Laravel's facades (e.g. Auth, View, Mail) I always get this exception:
我对 Laravel 5 和 PHPUnit 有奇怪的问题。当我尝试模拟 Laravel 的外观(例如 Auth、View、Mail)时,我总是遇到此异常:
Mockery\Exception\InvalidCountException: Method send("emails.register", array('user'=>'object(MCC\Models\Users\User)',), object(Closure)) from Mockery_0_Illuminate_Mail_Mailer should be called exactly 1 times but called 0 times.
Mockery\Exception\InvalidCountException:方法 send("emails.register", array('user'=>'object(MCC\Models\Users\User)',), object(Closure)) 来自 Mockery_0_Illuminate_Mail_Mailer 应该被精确调用 1 次但调用了0次。
I have a problem with "should be called exactly 1 times but called 0 times."
part. This is my test code:
我有"should be called exactly 1 times but called 0 times."
部分问题。这是我的测试代码:
public function testSendEmailToNewUserListener()
{
$user = factory(MCC\Models\Users\User::class)->create();
Mail::shouldReceive('send')
->with(
'emails.register',
['user' => $user],
function ($mail) use ($user) {
$mail->to($user->email, $user->name)
->subject('Thank you for registering an account.');
}
)
->times(1)
->andReturnUsing(function ($message) use ($user) {
dd($message);
$this->assertEquals('Thank you for registering an account.', $message->getSubject());
$this->assertEquals('mcc', $message->getTo());
$this->assertEquals(View::make('emails.register'), $message->getBody());
});
}
I put dd($message)
in closure because I want to know details about return value (how looks $message->getTo()
).
我dd($message)
关闭是因为我想知道有关返回值的详细信息(看起来如何$message->getTo()
)。
My TestCase class:
我的测试用例类:
<?php
/**
* Provides default values for functional tests.
*
* Class TestCase
*/
abstract class TestCase extends Illuminate\Foundation\Testing\TestCase
{
/**
* The base URL to use while testing the application.
*
* @var string
*/
protected $baseUrl = 'http://004-mcc.dev';
/**
* Creates the application.
*
* @return \Illuminate\Foundation\Application
*/
public function createApplication()
{
$app = require __DIR__ . '/../bootstrap/app.php';
$app->make(Illuminate\Contracts\Console\Kernel::class)->bootstrap();
\Illuminate\Support\Facades\Mail::pretend(TRUE);
return $app;
}
}
My phpunit.xml:
我的 phpunit.xml:
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
backupStaticAttributes="false"
bootstrap="bootstrap/autoload.php"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
syntaxCheck="false">
<testsuites>
<testsuite name="Application Test Suite">
<directory>./tests/</directory>
</testsuite>
<testsuite name="User">
<directory>./tests/UserRepository</directory>
</testsuite>
<testsuite name="User/Auth">
<directory>./tests/UserRepository/Auth</directory>
</testsuite>
<testsuite name="User/User">
<directory>./tests/UserRepository/User</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory suffix=".php">app/</directory>
</whitelist>
</filter>
<php>
<env name="APP_ENV" value="local"/>
<env name="CACHE_DRIVER" value="array"/>
<env name="SESSION_DRIVER" value="array"/>
<env name="QUEUE_DRIVER" value="sync"/>
</php>
</phpunit>
I checked many sources from Google, from Stackoverflow, most people mentioned about
我从 Google 和 Stackoverflow 上查了很多资料,大多数人都提到过
$this->app->instance('Illuminate\Mail\Mailer', $mockMailer)
but even this instruction doesn't help. Most of questions about this problem are not solved. I checked installed extensions, my Laravel is fresh installed (some models, some routes, about 20 tests).
但即使这条指令也无济于事。关于这个问题的大部分问题都没有解决。我检查了已安装的扩展,我的 Laravel 是全新安装的(一些模型,一些路由,大约 20 个测试)。
Also I tried methods like
我也尝试过类似的方法
->atLeast()
->times(1)
or
或者
->atLeast()
->once()
but nothing is works properly. Also instead of
但没有任何工作正常。也代替
Mail::shouldReceive('mail')
I used
我用了
$mailMock = Mockery::mock('Illuminate\Mail\Mailer');
$mailMock->shouldReceive('mail)
but these methods still not work.
但是这些方法还是不行。
Rest of console log:
控制台日志的其余部分:
/home/grzgajda/programowanie/php/005mcc/vendor/mockery/mockery/library/Mockery/CountValidator/Exact.php:37
/home/grzgajda/programowanie/php/005mcc/vendor/mockery/mockery/library/Mockery/Expectation.php:271
/home/grzgajda/programowanie/php/005mcc/vendor/mockery/mockery/library/Mockery/ExpectationDirector.php:120
/home/grzgajda/programowanie/php/005mcc/vendor/mockery/mockery/library/Mockery/Container.php:297
/home/grzgajda/programowanie/php/005mcc/vendor/mockery/mockery/library/Mockery/Container.php:282
/home/grzgajda/programowanie/php/005mcc/vendor/mockery/mockery/library/Mockery.php:142
/home/grzgajda/programowanie/php/005mcc/vendor/laravel/framework/src/Illuminate/Foundation/Testing/TestCase.php:48
/home/grzgajda/.composer/vendor/phpunit/phpunit/src/TextUI/Command.php:148
/home/grzgajda/.composer/vendor/phpunit/phpunit/src/TextUI/Command.php:100
Also I found one great advice (there Stackoverflow) but it doesn't work.
我还发现了一个很好的建议(有Stackoverflow),但它不起作用。
Mockery is by default a stubbing library, not a mocking one (which is confusing because of its name).
That means that ->shouldReceive(...) by default is "zero or more times". When using ->once(), you say it should be called zero or one time, but not more. This means it'll always pass.
When you want to assert that it is called once, you can use ->atLeast()->times(1) (one or more times) or ->times(1) (exactly one time)
默认情况下,Mockery 是一个 stubbing 库,而不是一个 mocking 库(因为它的名字而令人困惑)。
这意味着 ->shouldReceive(...) 默认情况下是“零次或多次”。当使用 ->once() 时,你说它应该被调用零次或一次,但不能更多。这意味着它会一直通过。
当你想断言它被调用一次时,你可以使用 ->atLeast()->times(1)(一次或多次)或 ->times(1)(恰好一次)
My php version: PHP 5.6.14-1+deb.sury.org~trusty+1 (cli)
我的 php 版本: PHP 5.6.14-1+deb.sury.org~trusty+1 (cli)
My apache: Server version: Apache/2.4.16 (Ubuntu)
我的阿帕奇: Server version: Apache/2.4.16 (Ubuntu)
Mockery version (from composer): "mockery/mockery": "0.9.*"
嘲讽版本(来自作曲家): "mockery/mockery": "0.9.*"
Laravel framework (from composer): "laravel/framework": "5.1.*"
Laravel 框架(来自作曲家): "laravel/framework": "5.1.*"
回答by samlev
Looking at your test case:
查看您的测试用例:
public function testSendEmailToNewUserListener()
{
$user = factory(MCC\Models\Users\User::class)->create();
Mail::shouldReceive('send')
->with(
'emails.register',
['user' => $user],
function ($mail) use ($user) {
$mail->to($user->email, $user->name)
->subject('Thank you for registering an account.');
}
)
->times(1)
->andReturnUsing(function ($message) use ($user) {
dd($message);
$this->assertEquals('Thank you for registering an account.', $message->getSubject());
$this->assertEquals('mcc', $message->getTo());
$this->assertEquals(View::make('emails.register'), $message->getBody());
});
}
Either:
任何一个:
- Creating the user calls the
Mail
facade, in which case you're calling that facade beforeyou're mocking it up.
- 创建用户调用
Mail
外观,在这种情况下,您在模拟它之前调用该外观。
OR
或者
- You aren't calling the function which calls the
Mail
facade.
- 您不是在调用调用
Mail
外观的函数。
Either way, Mail::shouldReceive('send')
should notbe the last thing in the test case.
无论哪种方式,Mail::shouldReceive('send')
应该不会是在测试用例的最后一件事。
The error you are getting is because Mail
is expecting the call to happen afteryou call ::shouldRecieve()
, but it's not - the test case ends, and the Mockery
instance has never been called.
您收到的错误是因为Mail
期望在您调用之后调用发生::shouldRecieve()
,但事实并非如此 - 测试用例结束,并且Mockery
从未调用过实例。
You could try something like this instead:
你可以尝试这样的事情:
public function testSendEmailToNewUserListener()
{
$testCase = $this;
Mail::shouldReceive('send')
->times(1)
->andReturnUsing(function ($message) use ($testCase) {
$testCase->assertEquals('Thank you for registering an account.', $message->getSubject());
$testCase->assertEquals('mcc', $message->getTo());
$testCase->assertEquals(View::make('emails.register'), $message->getBody());
});
$user = factory(MCC\Models\Users\User::class)->create();
}
回答by Alex Kyriakidis
To integrate Mockery with PhpUnit, you just need to define a tearDown() method for your tests containing the following:
要将 Mockery 与 PhpUnit 集成,您只需为包含以下内容的测试定义一个 tearDown() 方法:
public function tearDown() {
\Mockery::close();
}
This static call cleans up the Mockery container used by the current test, and run any verification tasks needed for your expectations.
此静态调用会清理当前测试使用的 Mockery 容器,并运行您期望所需的任何验证任务。
You can find more information about Mockery and PhpUnit integation here.
您可以在此处找到有关 Mockery 和 PhpUnit 集成的更多信息。