Symfony2 : Doctrine : PHPUnit : 在单元测试中使用模拟实体管理器刷新期间设置实体 Id [英] Symfony2 : Doctrine : PHPUnit : Set entity Id during flushing with mocked entity manager in unit tests

查看:32
本文介绍了Symfony2 : Doctrine : PHPUnit : 在单元测试中使用模拟实体管理器刷新期间设置实体 Id的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Symfony 2.8.13/Doctrine ORM 2.5.5/PHPUnit 5.7.5

Symfony 2.8.13 / Doctrine ORM 2.5.5 / PHPUnit 5.7.5

我想测试一个使用了学说实体管理器的类的方法.这个公共方法调用一个私有方法来实例化一个 Bookmark 实体,刷新它并返回这个实体.然后,在经过测试的方法中,我需要访问实体 Id.除了 Bookmark 实体本身之外,所有东西都被模拟了.主要问题是我的实体中没有 setId() 方法.这是代码和我解决这个问题的主要想法,但我不知道它是否正确?

I want to test a method of a class that makes use of the doctrine entity manager. This public method calls a private one that instantiates a Bookmark entity, flushes it and returns this entity. Then later, in the tested method I need to access the entity Id. Everything is mocked excepted the Bookmark entity itself. The main problem is that there is no setId() method in my entity. Here is the code and my main idea to solve this issue but I don't know if it is correct ?

经过测试的类和方法

class BookmarkManager
{
    //...

    public function __construct(TokenStorageInterface $tokenStorage, ObjectManager $em, Session $session)
    {
        //...
    }

    public function manage($bookmarkAction, $bookmarkId, $bookmarkEntity, $bookmarkEntityId)
    {
        //...
        $bookmark = $this->add($bookmarkEntity, $bookmarkEntityId);
        //...
        $bookmarkId = $bookmark->getId();
        //...
    }

    private function add($entity, $entityId)
    {
        //...
        $bookmark = new Bookmark();
        //...
        $this->em->persist($bookmark);
        $this->em->flush();

        return $bookmark;
    }
}

测试

class BookmarkManagerTest extends \PHPUnit_Framework_TestCase
{
    public function testThatRestaurantAdditionToBookmarksIsWellManaged()
    {
        //...
        //  THIS WON'T WORK AS NO setId() METHOD EXISTS
        $entityManagerMock->expects($this->once())
            ->method('persist')
            ->will($this->returnCallback(function ($bookmark) {
                if ($bookmark instanceof Bookmark) {
                    $bookmark->setId(1);
                }
            }));
        //...
        $bookManager = new BookmarkManager($tokenStorageMock, $entityManagerMock, $sessionMock);
        //...
    }
}

解决方案?

1- 按照此处的建议使用反射类:

1- Make usage of reflection class as proposed here :

$entityManagerMock->expects($this->once())
    ->method('persist')
    ->will($this->returnCallback(function ($bookmark) {
        if ($bookmark instanceof Bookmark) {
            $class = new \ReflectionClass($bookmark);
            $property = $class->getProperty('id');
            $property->setAccessible(true);
            $property->setValue($bookmark, 1);
            //$bookmark->setId(1);
        }
    }));

2- 创建一个从真实实体扩展而来的测试书签实体,并添加一个 setId() 方法.然后创建一个这个类的模拟,并用这个替换和自定义从 ReturnCallback 方法获得的那个?好像很烂...

2- Create a test Boookmark entity that extends from the real one and add a setId() method. Then create a mock of this class and replace and customize the one got from the ReturnCallback method with this one ? It seems crappy...

有什么想法吗?感谢您的帮助.

Any thoughts ? Thanks for your help.

推荐答案

反射看起来很有趣,但它降低了测试的可读性(与模拟混合会使情况变得困难).

The reflection looks interesting but it decreases readability of tests (mixing with mocks makes the situation tough).

我会为实体管理器创建一个假的,并根据反射实现那里的设置 id:

I would create a fake for entity manager and implements there setting id based on reflection:

class MyEntityManager implements ObjectManager
{
    private $primaryIdForPersitingObject;

    public function __construct($primaryIdForPersitingObject)
    {
        $this->primaryIdForPersitingObject = $primaryIdForPersitingObject;
    }

    ...

    public function persist($object)
    {
        $reflectionClass = new ReflectionClass(get_class($object));
        $idProperty = $reflectionClass->getProperty('id');
        $idProperty->setAccessible(true);
        $idProperty->setValue($object, $this->primaryIdForPersitingObject);
    }

    public function flush() { }

    ...
}

一旦你实现了这个,你就可以注入 MyEntityManager 的实例,让你的测试更小更容易维护.

Once you implemented this, you can inject the instance of MyEntityManager and make your tests small and easier to maintain.

你的测试看起来像

<?php

class BookmarkManagerTest extends \PHPUnit_Framework_TestCase
{
    public function testThatRestaurantAdditionToBookmarksIsWellManaged()
    {
        // ...
        $entityManager = MyEntityManager(1);
        //...
        $bookManager = new BookmarkManager($tokenStorageMock, $entityManager, $sessionMock);
        //...
    }
}

当然,如果需要为许多持久化对象设置不同的 id,情况可能会更困难.然后你可以,例如,在 persist 调用

Of course, a situation may be harder if there is a need of setting different ids for many persisting objects. Then you can, for example, increase $primaryIdForPersitingObject on persist call

public function persist($object)
{
    $reflectionClass = new ReflectionClass(get_class($object));
    $idProperty = $reflectionClass->getProperty('id');
    $idProperty->setAccessible(true);
    $idProperty->setValue($object, $this->primaryIdForPersitingObject);

    $this->primaryIdForPersitingObject++;
}

它可能会进一步扩展为每个实体类都有单独的 primaryIdForPersitingObject,并且您的测试将仍然是干净的.

It may be extended even further to have separate primaryIdForPersitingObject each entity class, and your tests will be still clean.

这篇关于Symfony2 : Doctrine : PHPUnit : 在单元测试中使用模拟实体管理器刷新期间设置实体 Id的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆