如何使用Symfony在serviceTest中模拟EntityManager?

时间:2016-12-03 17:10:57

标签: unit-testing phpunit symfony

抱歉,我的问题很长。

如何在扩展KernelTestCase的服务测试中模拟entityManager?

现在,解释和我的测试......

我正在使用Symfony3.2。我的申请是标准的。我有一些控制器,我使用WebTestCase来测试它们。

通常,我的Controller验证参数,调用服务/管理器,处理一些变量并推送它们进行查看,我的测试在扩展 WebTestCase 的测试中非常简单。

/**
 * Test New Game Action
 */
public function testFooAction(){
    //We mock the Service
    $fooService = $this
        ->getMockBuilder(GameService::class)
        ->disableOriginalConstructor()
        ->getMock();
    $fooService->expects(self::once())
        ->method('barMethod')
        ->willReturn($result);

    //We create the client
    $client = static::createClient();
    $container = $client->getContainer();

    //I put my mock here
    $container->set('app.game-service', $fooService);

    //I launch the request
    $client->request('GET', '/foo');
    //I handle the response
    $response = $client->getResponse();

    //I do some tests like this one
    self::assertEquals(200, $response->getStatusCode());

}

正如您所看到的,我不会调用EntityManger ,因为我使用服务并将这些行放入我的服务模拟

    //We create the client
    $client = static::createClient();
    $container = $client->getContainer();

    //I put my mock here
    $container->set('app.game-service', $fooService);

我在服务中模拟Entity Manager时遇到问题。我的控制器已经过测试但不是我的服务。

这是构造函数初始化一个简单的 protected 属性entityManager。问题是这个受保护的,你会看到更多......

   /**
     * FooService constructor.
     * @param EntityManager $entityManager
     */
    public function __construct(EntityManager $entityManager)
    {
           $this->entityManager = $entityManager;
    }

要测试我的服务,这是我的初始代码:

<?php
class FooServiceTest extends KernelTestCase
{
    /**
     * Foo Service.
     *
     * @var FooService
     */
    private $fooService;

    /**
     * Prepares the environment before running each test.
     */
    protected function setUp()
    {
        parent::setUp();

        self::bootKernel();
        $this->fooService = static::$kernel->getContainer()
            ->get('app.foo-service') //HERE IS HOW I HANDLE MY SERVICE TO TEST IT
        ;

    }

testStart工作正常,有一些数据库交互。

我知道我可以在测试期间回滚数据。但我想模拟entityManager 以验证 commit 方法的调用。

我试着在我的setUp中模拟实体管理器:

    protected function setUp()
    {
        parent::setUp();

        $entityManager = $this
            ->getMockBuilder('Doctrine\ORM\EntityManager')
            ->disableOriginalConstructor()
            ->getMock();
        $entityManager
            ->expects(once())  // I WANT EXACTLY ONE CALL TO COMMIT METHOD
            ->method('commit')  
            ->willReturn(null);

        self::bootKernel();
        $container = static::$kernel->getContainer();
        $container->set('doctrine.orm.entity_manager', $entityManager); // THIS LINE DOES NOTHING <=======
        $this->gameService = static::$kernel->getContainer()
            ->get('app.game-service')
        ;

Thes代码不起作用。模拟不到位。我仍然拥有真正的entityManager。我认为这是因为容器已经关闭。设置

是否有用

像野蛮人一样,我将entityManager属性更改为 public

我这样做:

    protected function setUp()
    {
        parent::setUp();

        $entityManagerMock = $this
            ->getMockBuilder('Doctrine\ORM\EntityManager')
            ->disableOriginalConstructor()
            ->getMock();
        $entityManagerMock
            ->expects(once())  // I WANT EXACTLY ONE CALL TO commit METHOD
            ->method('commit')  
            ->willReturn(null);

        self::bootKernel();
        $this->gameService = static::$kernel->getContainer()
            ->get('app.game-service')
        ;
        $this->gameService->entityManager = entityManagerMock;

完美无缺。可以运行测试。但是在公共财产中使用entityManager并不是一个好习惯

我的问题是:如何在服务测试中模拟entityManager?

(抱歉,我的英语不流利)

1 个答案:

答案 0 :(得分:2)

首先在你的代码中

$container->set('doctrine.orm.entity_manager'); // THIS LINE DOES NOTHING <=======

我确定你错过了第二个参数:)

另一个注意事项:不要嘲笑你不拥有的东西。 将来您将运行composer update,而EntityManager&#39; commit将有一些可选的isReallyCommit参数。您的代码将被破坏,但您不会注意到它,因为测试是绿色的。我知道这不太可能,但无论如何它只是一个例子。我认为这里的好习惯是

  • 将某个适配器与实体管理器封装在一起
  • 在服务单元测试中模拟该适配器
  • 使用针对真实数据库的功能测试来测试适配器,而不会嘲笑任何内容

或者只是不要单元测试您的服务,但使用真正的数据库交互进行功能测试