php - Mock 应该恰好被调用 1 次但被调用 0 次

标签 php laravel unit-testing mockery

我在使用 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.

我对 "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() 看起来如何)。

我的测试用例类:

<?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>

我检查了来自 Google、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 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)

我的 php 版本:PHP 5.6.14-1+deb.sury.org~trusty+1 (cli)

我的apache:服务器版本:Apache/2.4.16 (Ubuntu)

Mockery 版本(来自 Composer ):"mockery/mockery": "0.9.*"

Laravel 框架(来自 composer):"laravel/framework": "5.1.*"

最佳答案

查看您的测试用例:

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 facade,在这种情况下,您在 模拟它之前调用该 facade。

  • 您没有调用调用 Mail facade 的函数。

无论哪种方式,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();
}

关于php - Mock 应该恰好被调用 1 次但被调用 0 次,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33070381/

相关文章:

php - 大型 PHP 应用程序中的 SQL 调试

php - 在 Azure 上开发 PHP-MySQL 应用程序

php - Laravel 类属性没有返回响应

Laravel - 从本地化文件获取错误信息

javascript - 如何根据另一个选择结果更新 HTML 选择选项

php - 使用 PHP 将 jpg 图像转换为 gif、png 和 bmp 格式

javascript - 在 Laravel 中使用 React

node.js - 使用 Jest(和 mockgoose)测试 Node.js API

unit-testing - 在 golang 单元测试中模拟特定的错误类型

jquery - BackboneJS 中的路由器实例不同