在测试控制器中禁用DB写入 - 注入doctrine.orm.default_entity_manager会在激活FOSUser和HWIOAuth时抛出“致命错误”

时间:2014-09-20 00:04:14

标签: unit-testing doctrine-orm fosuserbundle entitymanager hwioauthbundle

我想在phpunit中对控制器进行功能测试时禁用数据库写入。

在Symfony2的一个项目中,我使用phpunit测试对保存到数据库的API端点的POST调用。这有效,我现在在激活FOSUser + HWIOAuth时出现回归问题。

为了避免phpunit将伪数据写入数据库,我在这里使用这个技巧:Testing Controllers in Symfony2 with Doctrine来模拟实体管理器,然后将模拟的服务注入到测试系统的容器中。

这样,当您运行测试时,数据库端点被模拟,并且只测试了一个flush(),这要归功于您在该问题和下面的代码中可以看到的$entityManagerMock->expects($this->once())->method('flush');。 / p>

这曾经有效。

此测试给出了绿色条,测试了POST调用,没有数据保存到数据库中。

public function testPostCreatesIssue()
{
    $client = static::createClient();

    $entityManagerMock = $this->getMockBuilder( 'Doctrine\ORM\EntityManager' )
        ->setMethods( array( 'persist', 'flush' ) )
        ->disableOriginalConstructor()
        ->getMock();

    $entityManagerMock->expects( $this->once() )
        ->method( 'flush' );

    $client->getContainer()->set( 'doctrine.orm.default_entity_manager', $entityManagerMock );

    $postData = array
    (
        'title' => 'Issues controller test, post creates issue',
        'body' => 'The test tests that a post call' . PHP_EOL . 'creates a new issue' . PHP_EOL . 'in the database.' . PHP_EOL . 'UTF8: áéíóú àèìòù ñç',
        'pageUrl' => 'file://Xmontero/AntiqueCrayon/MainBundle/Tests/Controller/IssuesControllerTest.php'
    );
    $crawler = $client->request( 'POST', '/issues/', $postData );
    $response = $client->getResponse();

    $this->assertEquals( Response::HTTP_CREATED, $response->getStatusCode() );
    $this->assertEquals( 'application/json', $response->headers->get( 'content-type' ) );
}

那一刻,我只有AppKernel.php

中的默认包+我自己的包

问题

当我在AppKernel.php

中激活这些捆绑包时
new FOS\UserBundle\FOSUserBundle(),
new HWI\Bundle\OAuthBundle\HWIOAuthBundle(),

并且在配置之后,一切都在我的浏览器中手动测试,包括执行上面测试的POST的ajax调用。该控制器与FOSUser和HWIOAuth无关,因为该控制器在我激活内核中的这些bundle之前就已经工作了。

但是尽管手动测试确实有效,但自动化测试开始失败,Call to a member function getRepository() on a non-object不在我的代码行中,而是在FOS和Doctrine的内部 - 这里是完整的输出:

xavi@bromo:/files/custom_www/antique-crayon/preproduction$ phpunit -c app --filter Post src/Xmontero/AntiqueCrayon/MainBundle/Tests/Controller/IssuesControllerTest.php
PHPUnit 3.6.10 by Sebastian Bergmann.

Configuration read from /files/custom_www/antique-crayon/preproduction/app/phpunit.xml.dist

PHP Fatal error:  Call to a member function getRepository() on a non-object in /files/custom_www/antique-crayon/preproduction/vendor/doctrine/orm/lib/Doctrine/ORM/EntityManager.php on line 759
PHP Stack trace:
PHP   1. {main}() /usr/bin/phpunit:0
PHP   2. PHPUnit_TextUI_Command::main() /usr/bin/phpunit:46
PHP   3. PHPUnit_TextUI_Command->run() /usr/share/php/PHPUnit/TextUI/Command.php:130
PHP   4. PHPUnit_TextUI_TestRunner->doRun() /usr/share/php/PHPUnit/TextUI/Command.php:192
PHP   5. PHPUnit_Framework_TestSuite->run() /usr/share/php/PHPUnit/TextUI/TestRunner.php:325
PHP   6. PHPUnit_Framework_TestSuite->runTest() /usr/share/php/PHPUnit/Framework/TestSuite.php:745
PHP   7. PHPUnit_Framework_TestCase->run() /usr/share/php/PHPUnit/Framework/TestSuite.php:772
PHP   8. PHPUnit_Framework_TestResult->run() /usr/share/php/PHPUnit/Framework/TestCase.php:751
PHP   9. PHPUnit_Framework_TestCase->runBare() /usr/share/php/PHPUnit/Framework/TestResult.php:649
PHP  10. PHPUnit_Framework_TestCase->runTest() /usr/share/php/PHPUnit/Framework/TestCase.php:804
PHP  11. ReflectionMethod->invokeArgs() /usr/share/php/PHPUnit/Framework/TestCase.php:942
PHP  12. Xmontero\AntiqueCrayon\MainBundle\Tests\Controller\IssuesControllerTest->testPostCreatesIssue() /files/custom_www/antique-crayon/preproduction/src/Xmontero/AntiqueCrayon/MainBundle/Tests/Controller/IssuesControllerTest.php:0
PHP  13. Symfony\Component\BrowserKit\Client->request() /files/custom_www/antique-crayon/preproduction/src/Xmontero/AntiqueCrayon/MainBundle/Tests/Controller/IssuesControllerTest.php:41
PHP  14. Symfony\Bundle\FrameworkBundle\Client->doRequest() /files/custom_www/antique-crayon/preproduction/vendor/symfony/symfony/src/Symfony/Component/BrowserKit/Client.php:327
PHP  15. Symfony\Component\HttpKernel\Client->doRequest() /files/custom_www/antique-crayon/preproduction/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Client.php:111
PHP  16. Symfony\Component\HttpKernel\Kernel->handle() /files/custom_www/antique-crayon/preproduction/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Client.php:81
PHP  17. Symfony\Component\HttpKernel\DependencyInjection\ContainerAwareHttpKernel->handle() /files/custom_www/antique-crayon/preproduction/app/bootstrap.php.cache:2330
PHP  18. Symfony\Component\HttpKernel\HttpKernel->handle() /files/custom_www/antique-crayon/preproduction/app/bootstrap.php.cache:3080
PHP  19. Symfony\Component\HttpKernel\HttpKernel->handleRaw() /files/custom_www/antique-crayon/preproduction/app/bootstrap.php.cache:2931
PHP  20. Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcher->dispatch() /files/custom_www/antique-crayon/preproduction/app/bootstrap.php.cache:2958
PHP  21. Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcher->preProcess() /files/custom_www/antique-crayon/preproduction/vendor/symfony/symfony/src/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcher.php:107
PHP  22. Symfony\Component\EventDispatcher\ContainerAwareEventDispatcher->getListeners() /files/custom_www/antique-crayon/preproduction/vendor/symfony/symfony/src/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcher.php:215
PHP  23. Symfony\Component\EventDispatcher\ContainerAwareEventDispatcher->lazyLoad() /files/custom_www/antique-crayon/preproduction/vendor/symfony/symfony/src/Symfony/Component/EventDispatcher/ContainerAwareEventDispatcher.php:128
PHP  24. Symfony\Component\DependencyInjection\Container->get() /files/custom_www/antique-crayon/preproduction/vendor/symfony/symfony/src/Symfony/Component/EventDispatcher/ContainerAwareEventDispatcher.php:188
PHP  25. appTestDebugProjectContainer->getProfilerListenerService() /files/custom_www/antique-crayon/preproduction/app/bootstrap.php.cache:2056
PHP  26. Symfony\Component\DependencyInjection\Container->get() /files/custom_www/antique-crayon/preproduction/app/cache/test/appTestDebugProjectContainer.php:2251
PHP  27. appTestDebugProjectContainer->getProfilerService() /files/custom_www/antique-crayon/preproduction/app/bootstrap.php.cache:2056
PHP  28. Symfony\Component\DependencyInjection\Container->get() /files/custom_www/antique-crayon/preproduction/app/cache/test/appTestDebugProjectContainer.php:2234
PHP  29. appTestDebugProjectContainer->getSecurity_ContextService() /files/custom_www/antique-crayon/preproduction/app/bootstrap.php.cache:2056
PHP  30. Symfony\Component\DependencyInjection\Container->get() /files/custom_www/antique-crayon/preproduction/app/cache/test/appTestDebugProjectContainer.php:2374
PHP  31. appTestDebugProjectContainer->getSecurity_Authentication_ManagerService() /files/custom_www/antique-crayon/preproduction/app/bootstrap.php.cache:2056
PHP  32. Symfony\Component\DependencyInjection\Container->get() /files/custom_www/antique-crayon/preproduction/app/cache/test/appTestDebugProjectContainer.php:3941
PHP  33. appTestDebugProjectContainer->getFosUser_UserProvider_UsernameEmailService() /files/custom_www/antique-crayon/preproduction/app/bootstrap.php.cache:2056
PHP  34. Symfony\Component\DependencyInjection\Container->get() /files/custom_www/antique-crayon/preproduction/app/cache/test/appTestDebugProjectContainer.php:3885
PHP  35. appTestDebugProjectContainer->getFosUser_UserManagerService() /files/custom_www/antique-crayon/preproduction/app/bootstrap.php.cache:2056
PHP  36. FOS\UserBundle\Doctrine\UserManager->__construct() /files/custom_www/antique-crayon/preproduction/app/cache/test/appTestDebugProjectContainer.php:1657
PHP  37. Doctrine\ORM\EntityManager->getRepository() /files/custom_www/antique-crayon/preproduction/vendor/friendsofsymfony/user-bundle/FOS/UserBundle/Doctrine/UserManager.php:40
xavi@bromo:/files/custom_www/antique-crayon/preproduction$

缩小问题范围

问题是我在测试阶段为其注入模拟的服务doctrine.orm.default_entity_manager,这就是为什么浏览器中的手动测试不会失败:没有模拟,那里。

如果我编辑我的测试不使用任何模拟,只需写入真正的数据库,它就可以工作:

要在没有模拟的情况下进行测试,我只需注释掉容器中的->set()。为避免Method was expected to be called 1 times, actually called 0 times.我还注释掉了->expects()

    //$entityManagerMock->expects( $this->once() )
    //  ->method( 'flush' );
    //
    //$client->getContainer()->set( 'doctrine.orm.default_entity_manager', $entityManagerMock );

然后测试greenbars:

xavi@bromo:/files/custom_www/antique-crayon/preproduction$ phpunit -c app --filter Post src/Xmontero/AntiqueCrayon/MainBundle/Tests/Controller/IssuesControllerTest.php
PHPUnit 3.6.10 by Sebastian Bergmann.

Configuration read from /files/custom_www/antique-crayon/preproduction/app/phpunit.xml.dist

.

Time: 0 seconds, Memory: 32.50Mb

OK (1 test, 2 assertions)
xavi@bromo:/files/custom_www/antique-crayon/preproduction$

结论

  • doctrine.orm.default_entity_manager预激活FOSUser和HWIOAuth中注入模拟,工作正常
  • AppKernel中激活这些捆绑包时,技巧无效。
  • 然后我只能测试允许将实际数据写入数据库,这在测试时是不可取的。

问题

当在内核中激活FOSUserBundle和HWIOAuthBundle时,如何在phpunit中的测试中调用控制器时禁止写入DataBase?

谢谢! 哈维。

1 个答案:

答案 0 :(得分:0)

我并不为此感到骄傲,但我没有用phpUnit做过这么多的测试,以100%的安全性回答你的问题,但这是我的看法。

我认为解决方案是模拟entityManager,就像你正在做的那样,但是你只是在使用persist()和flush()方法。

->setMethods( array( 'persist', 'flush' ) )
->disableOriginalConstructor()

如果没有您要测试的代码,我无法确定它,但似乎在您的控制器的任何部分中您使用的是de FOSUserBundle或HWIOAuthBundle,其中一个或两个至少使用了getRepository()方法,你需要至少模拟可以通过FOSUserBundle和HWIOAuthBundle使用的所有方法。

如果你没有用setMethods()来表示方法,那么所有这些方法都被模拟了,你仍然可以控制方法被调用的次数与否,如下面的代码示例所示。

$mockObjectManager = $this->getMockBuilder('Doctrine\ORM\EntityManager')
    ->disableOriginalConstructor()
    ->getMock();

$mockObjectManager->expects($this->any())
    ->method('persist');
$mockObjectManager->expects($this->atLeastOnce())
    ->method('flush');

如果FOSUserBundle试图获取存储库,就像看起来一样,你将不得不模拟该调用的返回,如果你不这样做,它可能会在下次调用时以错误结束,例如:

$this->em->expects($this->any())
        ->method('getRepository')
        ->with('FOSUserBundle:XXXXX')
        ->will($this->returnValue($this->getMockedClassWithFind('\Doctrine\ORM\EntityRepository')));

我认为最好的解决方案是开始解决第一个错误,你会看到另一个错误,因为FosUserBundle期望的对象将为null,并且模拟那个,并且可能期望另一个对象或者你可能已经完成了。 / p>

希望很有帮助。