gpt4 book ai didi

Symfony2 : Doctrine : PHPUnit : Set entity Id during flushing with mocked entity manager in unit tests

转载 作者:行者123 更新时间:2023-12-05 05:21:48 48 4
gpt4 key购买 nike

Symfony 2.8.13/Doctrine ORM 2.5.5/PHPUnit 5.7.5

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

测试的类和方法

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- 按照建议使用反射类 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 方法获得的模拟?看起来很糟糕......

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

最佳答案

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

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

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() { }

...
}

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

你的测试看起来像

<?php

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

当然,如果需要为许多持久对象设置不同的 id,情况可能会更困难。然后你可以,例如,增加 $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,并且您的测试仍然是干净的。

关于Symfony2 : Doctrine : PHPUnit : Set entity Id during flushing with mocked entity manager in unit tests,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42133082/

48 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com