ควรเรียกจำลอง 1 ครั้ง แต่เรียกว่า 0 ครั้ง

ฉันมีปัญหาแปลก ๆ กับ Laravel 5 และ PHPUnit เมื่อฉันพยายามเยาะเย้ยส่วนหน้าของ Laravel (เช่น Auth, View, Mail) ฉันจะได้รับข้อยกเว้นนี้เสมอ:

Mockery\Exception\InvalidCountException: วิธีการ send(emails.register, array('user'=›'object(MCC\Models\Users\User)',), object(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>

ฉันตรวจสอบแหล่งที่มามากมายจาก Google จาก Stackoverflow ที่คนส่วนใหญ่พูดถึง

$this->app->instance('Illuminate\Mail\Mailer', $mockMailer)

แต่ถึงแม้คำแนะนำนี้ก็ไม่ได้ช่วยอะไร คำถามส่วนใหญ่เกี่ยวกับปัญหานี้ไม่ได้รับการแก้ไข ฉันตรวจสอบส่วนขยายที่ติดตั้งแล้ว Laravel ของฉันได้รับการติดตั้งใหม่ (บางรุ่น บางเส้นทาง การทดสอบประมาณ 20 รายการ)

ฉันยังลองวิธีการเช่น

->atLeast()
->times(1)

or

->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) แต่มันไม่ทำงาน

การเยาะเย้ยโดยค่าเริ่มต้นแล้วจะเป็นห้องสมุดที่ขัดเขิน ไม่ใช่ห้องสมุดที่เยาะเย้ย (ซึ่งทำให้สับสนเพราะชื่อของมัน)

นั่นหมายความว่า -›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": "0.9.*"

กรอบงาน Laravel (จากผู้แต่ง): "laravel/framework": "5.1.*"


person Grzegorz Gajda    schedule 11.10.2015    source แหล่งที่มา


คำตอบ (2)


ดูกรณีทดสอบของคุณ:

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 ไม่เคยถูกเรียกใช้

คุณสามารถลองสิ่งนี้แทน:

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();
}
person samlev    schedule 20.10.2015

หากต้องการรวม Mockery เข้ากับ PhpUnit คุณเพียงแค่ต้องกำหนดเมธอด TearDown() สำหรับการทดสอบของคุณที่มีสิ่งต่อไปนี้:

public function tearDown() {
    \Mockery::close();
}

การเรียกแบบคงที่นี้จะล้างคอนเทนเนอร์ Mockery ที่ใช้ในการทดสอบปัจจุบัน และรันงานการตรวจสอบที่จำเป็นสำหรับความคาดหวังของคุณ

คุณสามารถดูข้อมูลเพิ่มเติมเกี่ยวกับการผสานรวม Mockery และ PhpUnit ได้ที่นี่

person Alex Kyriakidis    schedule 12.10.2015
comment
วิธีการในตัวของ Laravel สำหรับ tearDown() มี Mockery::close(); แต่ก็ยังไม่ได้ช่วยอะไร การสืบทอดเมธอดพาเรนต์ tearDown() และการเพิ่มฟังก์ชันที่จุดเริ่มต้น (หรือสิ้นสุด) ก็ไม่ได้ช่วยอะไรเช่นกัน จากเอกสารของ Mockery ฉันเอาผู้ฟังและวางเขาไว้ใน phpunit.xml ของฉัน แต่ก็ยังใช้งานไม่ได้ ยังคงเป็นข้อยกเว้นเหมือนเดิม - person Grzegorz Gajda; 12.10.2015
comment
คุณเคยลองปิดการเยาะเย้ยโดยไม่สืบทอดความใกล้ชิดของผู้ปกครองหรือไม่? - person Alex Kyriakidis; 12.10.2015
comment
ใช่ ในหลายรูปแบบ ฉันวาง Mockery::close() ที่ส่วนท้ายของวิธีการ กำหนดเอง tearDown() ในคลาส TestCase กำหนดเอง tearDown() ในคลาสทดสอบ และแน่นอนว่าผสมชุดค่าผสมเหล่านี้ ยังคงเป็นข้อยกเว้นเหมือนเดิม - person Grzegorz Gajda; 12.10.2015