我在Laravel 5和PHPUnit上遇到了奇怪的问题。当我试图模仿Laravel的外墙(例如Auth,View,Mail)时,我总是得到这个例外:
Mockery \ Exception \ InvalidCountException:方法发送(“emails.register”,数组('用户'=>'对象(MCC \ Models \ Users \ User)',),对象(Closure))来自Mockery_0_Illuminate_Mail_Mailer应该是叫 恰好1次,但被称为0次。
我对"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());
});
}
我将dd($message)
置于关闭状态,因为我想了解有关返回值的详细信息(如何查看$message->getTo()
)。
我的TestCase类:
<?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;
}
}
我的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>
我查了很多来自谷歌的资料,来自Stackoverflow,大多数人都提到了
$this->app->instance('Illuminate\Mail\Mailer', $mockMailer)
但即使这条指令也无济于事。关于这个问题的大多数问题都没有解决。我检查了安装的扩展,我的Laravel是全新安装的(一些型号,一些路线,大约20个测试)。
我也试过像
这样的方法->atLeast()
->times(1)
或
->atLeast()
->once()
但没有什么是正常的。而不是
Mail::shouldReceive('mail')
我用过
$mailMock = Mockery::mock('Illuminate\Mail\Mailer');
$mailMock->shouldReceive('mail)
但这些方法仍无效。
其余的控制台日志:
/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
我也找到了一个很好的建议(那里Stackoverflow),但它不起作用。
默认情况下,Mockery是一个存根库,而不是一个模拟库(由于其名称而令人困惑)。
这意味着 - &gt; shouldReceive(...)默认为“零次或多次”。当使用 - &gt; once()时,你会说它应该被称为零或一次,但不是更多。这意味着它将永远通过。
如果您想断言它被调用一次,您可以使用 - &gt; atLeast() - &gt; times(1)(一次或多次)或 - &gt; times(1)(恰好一次)< / p>
我的php版本:PHP 5.6.14-1+deb.sury.org~trusty+1 (cli)
我的阿帕奇:Server version: Apache/2.4.16 (Ubuntu)
Mockery版本(来自作曲家):"mockery/mockery": "0.9.*"
Laravel框架(来自作曲家):"laravel/framework": "5.1.*"
答案 0 :(得分:8)
查看您的测试用例:
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());
});
}
或者:
Mail
外观,在这种情况下,您在之前调用该外观。您正在嘲笑它。OR
Mail
外观的函数。无论哪种方式,Mail::shouldReceive('send')
都不应该是测试用例中的最后一件事。
你得到的错误是因为Mail
期待调用在之后调用::shouldRecieve()
,但事实并非如此 - 测试用例结束,Mockery
1}}实例从未被调用过。
你可以尝试这样的事情:
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();
}
答案 1 :(得分:2)
要将Mockery与PhpUnit集成,您只需为包含以下内容的测试定义tearDown()方法:
public function tearDown() {
\Mockery::close();
}
此静态调用会清除当前测试使用的Mockery容器,并运行您期望所需的任何验证任务。
您可以找到有关Mockery和PhpUnit整合here的更多信息。