模拟函数被调用,但 expects($this->once()) 失败

mock function called, but expects($this->once()) fails

我正在测试用于发送提醒文本消息的 Symfony 命令。为此,我为我的短信界面创建了一个服务,并模拟了容器和短信服务:

正在测试的函数

protected function textReminders()
{
    $mailer = $this->getContainer()->get('mailer');
    $em = $this->getContainer()->get( 'doctrine' )->getManager();


    if ($this->getContainer()->get('kernel')->getEnvironment() == 'dev'){
        $debug = true;
    }else{
        $debug = false;
    }

    $textMessage = $this->getContainer()->get('text_messaging.interface');
    $textMessage->sendSMS( $target, $content, $debug);

}

测试

private function getMockContainer()
{
    $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\Container')
                      ->disableOriginalConstructor()
                      ->setMethods(array('get'))
                      ->getMock();

    return $container;
}

protected function setupMocks()
{
    $mockText = $this->getMockBuilder('TextaHQ')
                     ->disableOriginalConstructor()
                     ->setMethods(array('sendSMS'))
                     ->getMock();
    $mockContainer = $this->getMockContainer();

    $container = self::$kernel->getContainer();
    $mockContainer->method('get')
        ->withConsecutive(['mailer'], ['doctrine'], ['kernel'], ['text_messaging.interface'])
        ->willReturnOnConsecutiveCalls(
            $this->returnValue($container->get('mailer')),
            $this->returnValue($container->get('doctrine')),
            $this->returnValue(self::$kernel),
            $this->returnValue($mockText))
    ;

    $this->setMyMock([
                         'text' => $mockText,
                         'container' => $mockContainer
                     ]);
}

public function testExecute()
{
    $this->setupMocks();
    self::bootKernel();
    $application = new Application(self::$kernel);

    $application->add(new ActionRemindCommand());

    $command = $application->find( 'ahp:remind' );
    $command->setContainer($this->getMyMock()['container']);
    $commandTester = new CommandTester( $command );

    $commandTester->execute( array(
                                     'command' => $command->getName(),
                                     'type' => 'text'
                                 ) );
    $output = $commandTester->getDisplay();

    $this->assertions();
}

protected function assertions()
{
    $this->getMyMock()['text']
        ->expects( $this->once() )
            ->method( 'sendSMS' )
            ;

}

更新测试,全部在一个文件中

public function testExecute()
{
    $insertSql = 'echo "'
        . str_replace(
            array('"'    ,'`'  ),
            array('\"'  ,'\`'),
            $this->getPrepSql() )
        . '" | mysql ahp_example_com';
    exec($insertSql);

    self::bootKernel();

    $mockText = $this->getMockBuilder('TextaHQ')
                     ->disableOriginalConstructor()
                     ->setMethods(array('sendSMS'))
                     ->getMock();
    $mockContainer = $this->getMockBuilder('Symfony\Component\DependencyInjection\Container')
                                       ->disableOriginalConstructor()
                                       ->setMethods(array('get'))
                                       ->getMock();

    $container = self::$kernel->getContainer();
    $mockContainer->method('get')
                  ->withConsecutive(['mailer'], ['doctrine'], ['kernel'], ['text_messaging.interface'])
                  ->willReturnOnConsecutiveCalls(
                      $this->returnValue($container->get('mailer')),
                      $this->returnValue($container->get('doctrine')),
                      $this->returnValue(self::$kernel),
                      $this->returnValue($mockText))
    ;


    $application = new Application(self::$kernel);

    $application->add(new ActionRemindCommand());

    $mailer = self::$kernel->getContainer()->get('swiftmailer.mailer');
    $logger = new \Swift_Plugins_MessageLogger();
    $mailer->registerPlugin( $logger );
    $this->setMailCollector($logger);

    $output = '';
    for($i=1;$i<=$this->getRunNoTimes();$i++) {
        $command = $application->find( 'ahp:remind' );
        $command->setContainer($mockContainer);
        $commandTester = new CommandTester( $command );

        $commandTester->execute( array(
                                     'command' => $command->getName(),
                                     'type' => 'text'
                                 ) );
        $output .= $commandTester->getDisplay();
    }

    $mockText
        ->expects( $this->once() )
        ->method( 'sendSMS' )
    ;
}

**PHPStorm 测试调用**

/usr/bin/php     /home/jochen/projects/ahp/trunk/vendor/phpunit/phpunit/phpunit --configuration /home/jochen/projects/ahp/trunk/app/phpunit.xml.dist AgriHealth\AhpBundle\Tests\Command\ActionRemindCommandVet7DaysBeforeTest /home/jochen/projects/ahp/trunk/src/AgriHealth/AhpBundle/Tests/Command/RemindText/ActionRemindCommandVet7DaysBeforeTest.php --teamcity
Testing started at 10:25 AM ...
PHPUnit 5.5.4 by Sebastian Bergmann and contributors.


Expectation failed for method name is equal to <string:sendSMS> when invoked 1 time(s).
Method was expected to be called 1 times, actually called 0 times.



Time: 1.12 seconds, Memory: 18.00MB


FAILURES!
Tests: 1, Assertions: 1, Failures: 1.

Process finished with exit code 1

当我调试测试时,我可以看到 $textMessage 是一个 mock。

但是在 assertions() 的测试结束时,我得到一个错误:

Expectation failed for method name is equal to <string:sendsms> when invoked 1 time(s).
Method was expected to be called 1 times, actually called 0 times.

调试器将模拟函数显示为小写:"sendsms",但重命名函数没有帮助。

这里有一个小的说明性案例来澄清我在评论中提到的要点。对于在执行实际调用方法后设置期望值的情况,它也有一个失败的测试(这看起来是你的多合一方法更新测试的情况。

class MyClass
{
    public function someMethod(){

    }
}

class ExpectationsTest extends PHPUnit_Framework_TestCase
{
    private $myClassMock;

    public function setUp(){
        $this->myClassMock = $this->getMock('MyClass');
    }

    public function testLocalMock(){
        $localMock = $this->getMock('MyClass');
        $localMock->expects($this->once())
                  ->method('someMethod');
        $localMock->someMethod();
    }

    public function testClassScopeMockInstance(){
        $this->myClassMock->expects($this->once())
                          ->method('someMethod');
        $this->myClassMock->someMethod();
    }

    public function testWillFailBecauseExpectationWasSetAfterCall(){
        $this->myClassMock->someMethod();
        $this->myClassMock->expects($this->once())
                          ->method('someMethod');
    }

    public function testCanUseHelperToCreateLocalMock(){
        $mock = $this->createMyClassMock();
        $mock->expects($this->once())
             ->method('someMethod');
        $mock->someMethod();
    }

        private function createMyClassMock(){
            return $this->getMock('MyClass');
        }

    public function testCanSetExpectationsInHelper(){
        $this->setExpecatationsOnTestCaseScopeMock();
        $this->myClassMock->someMethod();
    }

        private function setExpecatationsOnTestCaseScopeMock(){
            $this->myClassMock->expects($this->once())
                              ->method('someMethod');
        }
}

顺便说一句,我认为我第一次没有足够彻底地探索您的代码。我想我可能错误地假设了您的 setupMocksgetMyMock 是如何工作的。抱歉。