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

Grz*_*jda 5 php unit-testing laravel 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());
        });
}
Run Code Online (Sandbox Code Playgroud)

我放弃了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;
    }
}
Run Code Online (Sandbox Code Playgroud)

我的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>
Run Code Online (Sandbox Code Playgroud)

我检查了很多人提到的来自Google的许多来源,来自Stackoverflow

$this->app->instance('Illuminate\Mail\Mailer', $mockMailer)
Run Code Online (Sandbox Code Playgroud)

但即使这条指令也无济于事.关于这个问题的大多数问题都没有解决.我检查了安装的扩展,我的Laravel是全新安装的(一些型号,一些路线,大约20个测试).

我也试过像这样的方法

->atLeast()
->times(1)
Run Code Online (Sandbox Code Playgroud)

要么

->atLeast()
->once()
Run Code Online (Sandbox Code Playgroud)

但没有什么是正常的.而不是

Mail::shouldReceive('mail')
Run Code Online (Sandbox Code Playgroud)

我用了

$mailMock = Mockery::mock('Illuminate\Mail\Mailer');
$mailMock->shouldReceive('mail)
Run Code Online (Sandbox Code Playgroud)

但这些方法仍然不起作用.

其余控制台日志:

/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
Run Code Online (Sandbox Code Playgroud)

此外,我发现了一个很好的建议(有Stackoverflow),但它不起作用.

默认情况下,Mockery是一个存根库,而不是一个模拟库(由于它的名称而令人困惑).

这意味着 - > shouldReceive(...)默认为"零次或多次".当使用 - > once()时,你会说它应该被称为零或一次,但不是更多.这意味着它将永远通过.

当你想断言它被调用一次时,你可以使用 - > atLeast() - > times(1)(一次或多次)或 - > times(1)(恰好一次)

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

sam*_*lev 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());
        });
}
Run Code Online (Sandbox Code Playgroud)

或者:

  • 创建用户会调用Mail外观,在这种情况下,您在模拟它之前调用该外观.

要么

  • 您没有调用调用Mail外观的函数.

无论哪种方式,Mail::shouldReceive('send')应该不会是在测试用例的最后一件事.

您得到的错误是因为Mail期望您调用之后发生调用::shouldRecieve(),但事实并非如此 - 测试用例结束,并且Mockery实例从未被调用过.

您可以尝试这样的事情:

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();
}
Run Code Online (Sandbox Code Playgroud)