PHPUnit: How do I mock an EntityManager without mocking the Metadata?
使用 PHPUnit 和 Doctrine 我经常最终编写非常大的方法来模拟 Doctrine
所以这是我的问题:如何在不需要数据库连接的情况下通过
我正在将
示例:
1 2 3 4 5 6 7 8 9 | public function testGetUniqueFields() { $this->prepareGetUniqueFields(); // about 50 lines of mocking ClassMetadata $entity = 'UniqueWithoutAssociation'; $unique = $this->handler->getUniqueFields($entity); $expected = ["uniqueColumn"]; $this->assertEquals($expected, $unique, 'getUniqueFields does not return the unique fields'); } |
以及实际类的代码:
1 2 3 4 5 6 7 8 9 10 11 12 | public function getUniqueFields($class) { $unique = array(); $metadata = $this->getClassMetadata($class); $fields = $metadata->getFieldNames(); foreach ($fields as $field) { if($metadata->isUniqueField($field) && !$metadata->isIdentifier($field)) { $unique[] = $field; } } return $unique; } |
测试按预期工作,但每次我测试另一种方法或该方法的另一种行为时,我都需要再次准备模拟或结合过去的定义。另外,这段代码我需要的 50 行代码是我在这个测试中最少的。大多数测试类都是关于
在花了很多时间研究 Doctrine 源代码之后,我找到了一个解决方案。
再一次,这个解决方案只适用于你经常使用 Doctrines
不过,由于作曲家的最低稳定性设置被设置为稳定,这样的组件可以被视为稳定,因此没有绝对需要创建模拟对象。
注入的
-
Doctrine\\ORM\\Configuration 获取实体路径-
Doctrine\\Common\\Annotations\\AnnotationReader 和Doctrine\\ORM\\Mapping\\Driver\\AnnotationDriver 通过Configuration 对象注入
-
-
Doctrine\\DBAL\\Connection 获取数据库平台以了解标识符策略。这个对象应该被模拟,所以不可能有数据库调用-
Doctrine\\DBAL\\Platforms\\AbstractPlatform 如前所述
-
-
Doctrine\\Common\\EventManager 触发一些事件
对于单个测试方法或简单方法调用,我创建了一个返回
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | /** * @return EntityManager|\\PHPUnit_Framework_MockObject_MockObject */ public function getEmMock() { $dir = __DIR__."/Asset/Entity/"; $config = Setup::createAnnotationMetadataConfiguration(array($dir), true); $eventManager = new \\Doctrine\\Common\\EventManager(); $platform = new PostgreSqlPlatform(); $metadataFactory = new ClassMetadataFactory(); $config->setMetadataDriverImpl(new AnnotationDriver(new AnnotationReader())); $connectionMock = $this->getMockBuilder('Doctrine\\DBAL\\Connection') ->disableOriginalConstructor() ->getMock(); $connectionMock->expects($this->any()) ->method('getDatabasePlatform') ->will($this->returnValue($platform)); /** @var EntityManager|\\PHPUnit_Framework_MockObject_MockObject $emMock */ $emMock = $this->getMockBuilder('Doctrine\\ORM\\EntityManager') ->disableOriginalConstructor() ->getMock(); $metadataFactory->setEntityManager($emMock); $emMock->expects($this->any()) ->method('getConfiguration') ->will($this->returnValue($config)); $emMock->expects($this->any()) ->method('getConnection') ->will($this->returnValue($connectionMock)); $emMock->expects($this->any()) ->method('getEventManager') ->will($this->returnValue($eventManager)); $emMock->expects($this->any()) ->method('getClassMetadata') ->will($this->returnCallback(function($class) use ($metadataFactory){ return $metadataFactory->getMetadataFor($class); })); return $emMock; } |
在这里,您甚至可以通过调用为
为了进一步的需要,我创建了一个抽象类,它提供了最大的灵活性,并允许您模拟其他所有内容或以完全不同的方式创建一些组件。
它需要两个配置:实体路径和平台对象。您可以通过在
有点大,但这里是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 | use Doctrine\\Common\\Annotations\\AnnotationReader; use Doctrine\\Common\\EventManager; use Doctrine\\DBAL\\Connection; use Doctrine\\DBAL\\Platforms\\AbstractPlatform; use Doctrine\\ORM\\Configuration; use Doctrine\\ORM\\EntityManager; use Doctrine\\ORM\\Mapping\\ClassMetadataFactory; use Doctrine\\ORM\\Mapping\\Driver\\AnnotationDriver; use Doctrine\\ORM\\Tools\\Setup; /** * Class AbstractTestWithMetadata * @author Marius Teller */ abstract class AbstractTestWithMetadata extends \\PHPUnit_Framework_TestCase { const EXCEPTION_NO_ENTITY_PATHS_SET ="At least one entity path must be set"; const EXCEPTION_NO_PLATFORM_SET ="An instance of Doctrine\\\\DBAL\\\\Platforms\\\\AbstractPlatform must be set"; /** * @var array */ protected $entityPaths = []; /** * @var AbstractPlatform */ protected $platform; /** * @var EntityManager */ protected $emMock; /** * @var Connection */ protected $connectionMock; /** * @var Configuration */ protected $configuration; /** * @var EventManager */ protected $eventManager; /** * @var ClassMetadataFactory */ protected $classMetadataFactory; /** * @return array * @throws \\Exception */ public function getEntityPaths() { if($this->entityPaths === []) { throw new \\Exception(self::EXCEPTION_NO_ENTITY_PATHS_SET); } return $this->entityPaths; } /** * @param array $entityPaths */ public function setEntityPaths(array $entityPaths) { $this->entityPaths = $entityPaths; } /** * add an entity path * @param string $path */ public function addEntityPath($path) { $this->entityPaths[] = $path; } /** * @return AbstractPlatform * @throws \\Exception */ public function getPlatform() { if(!isset($this->platform)) { throw new \\Exception(self::EXCEPTION_NO_PLATFORM_SET); } return $this->platform; } /** * @param AbstractPlatform $platform */ public function setPlatform(AbstractPlatform $platform) { $this->platform = $platform; } /** * @return EntityManager */ public function getEmMock() { if(!isset($this->emMock)) { /** @var EntityManager|\\PHPUnit_Framework_MockObject_MockObject $emMock */ $emMock = $this->getMockBuilder('Doctrine\\ORM\\EntityManager') ->disableOriginalConstructor() ->getMock(); $config = $this->getConfiguration(); $connectionMock = $this->getConnectionMock(); $eventManager = $this->getEventManager(); $classMetadataFactory = $this->getClassMetadataFactory(); $classMetadataFactory->setEntityManager($emMock); $emMock->expects($this->any()) ->method('getConfiguration') ->will($this->returnValue($config)); $emMock->expects($this->any()) ->method('getConnection') ->will($this->returnValue($connectionMock)); $emMock->expects($this->any()) ->method('getEventManager') ->will($this->returnValue($eventManager)); $emMock->expects($this->any()) ->method('getClassMetadata') ->will($this->returnCallback(function($class) use ($classMetadataFactory){ return $classMetadataFactory->getMetadataFor($class); })); $this->setEmMock($emMock); } return $this->emMock; } /** * @param EntityManager $emMock */ public function setEmMock($emMock) { $this->emMock = $emMock; } /** * @return Connection */ public function getConnectionMock() { if(!isset($this->connectionMock)) { $platform = $this->getPlatform(); /** @var Connection|\\PHPUnit_Framework_MockObject_MockObject $connectionMock */ $connectionMock = $this->getMockBuilder('Doctrine\\DBAL\\Connection') ->disableOriginalConstructor() ->getMock(); $connectionMock->expects($this->any()) ->method('getDatabasePlatform') ->will($this->returnValue($platform)); $this->setConnectionMock($connectionMock); } return $this->connectionMock; } /** * @param Connection $connectionMock */ public function setConnectionMock($connectionMock) { $this->connectionMock = $connectionMock; } /** * @return Configuration */ public function getConfiguration() { if(!isset($this->configuration)) { $config = Setup::createAnnotationMetadataConfiguration($this->getEntityPaths(), true); $config->setMetadataDriverImpl(new AnnotationDriver(new AnnotationReader())); $this->setConfiguration($config); } return $this->configuration; } /** * @param Configuration $configuration */ public function setConfiguration(Configuration $configuration) { $this->configuration = $configuration; } /** * @return EventManager */ public function getEventManager() { if(!isset($this->eventManager)) { $this->setEventManager(new EventManager()); } return $this->eventManager; } /** * @param EventManager $eventManager */ public function setEventManager($eventManager) { $this->eventManager = $eventManager; } /** * @return ClassMetadataFactory */ public function getClassMetadataFactory() { if(!isset($this->classMetadataFactory)) { $this->setClassMetadataFactory(new ClassMetadataFactory()); } return $this->classMetadataFactory; } /** * @param ClassMetadataFactory $classMetadataFactory */ public function setClassMetadataFactory(ClassMetadataFactory $classMetadataFactory) { $this->classMetadataFactory = $classMetadataFactory; } } |
另一个提示:您可能对其他类的注释有问题,例如