模拟应该被称为1次,但被称为0次

时间:2015-10-11 21:39:14

标签: php laravel unit-testing mockery

我在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.*"

2 个答案:

答案 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的更多信息。