PageRenderTime 46ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 1ms

/lib/internal/Magento/Framework/TestFramework/Unit/Helper/ObjectManager.php

https://gitlab.com/crazybutterfly815/magento2
PHP | 345 lines | 217 code | 26 blank | 102 comment | 22 complexity | 36c652b09dd331984390568a8d89e946 MD5 | raw file
  1. <?php
  2. /**
  3. * Copyright © 2016 Magento. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. /**
  7. * Helper class for basic object retrieving, such as blocks, models etc...
  8. */
  9. namespace Magento\Framework\TestFramework\Unit\Helper;
  10. /**
  11. * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
  12. */
  13. class ObjectManager
  14. {
  15. /**
  16. * Special cases configuration
  17. *
  18. * @var array
  19. */
  20. protected $_specialCases = [
  21. \Magento\Framework\Model\ResourceModel\AbstractResource::class => '_getResourceModelMock',
  22. \Magento\Framework\TranslateInterface::class => '_getTranslatorMock',
  23. ];
  24. /**
  25. * Test object
  26. *
  27. * @var \PHPUnit_Framework_TestCase
  28. */
  29. protected $_testObject;
  30. /**
  31. * Class constructor
  32. *
  33. * @param \PHPUnit_Framework_TestCase $testObject
  34. */
  35. public function __construct(\PHPUnit_Framework_TestCase $testObject)
  36. {
  37. $this->_testObject = $testObject;
  38. }
  39. /**
  40. * Get mock for argument
  41. *
  42. * @param string $argClassName
  43. * @param array $originalArguments
  44. * @return null|object|\PHPUnit_Framework_MockObject_MockObject
  45. */
  46. protected function _createArgumentMock($argClassName, array $originalArguments)
  47. {
  48. $object = null;
  49. if ($argClassName) {
  50. $object = $this->_processSpecialCases($argClassName, $originalArguments);
  51. if (null === $object) {
  52. $object = $this->_getMockWithoutConstructorCall($argClassName);
  53. }
  54. }
  55. return $object;
  56. }
  57. /**
  58. * Process special cases
  59. *
  60. * @param string $className
  61. * @param array $arguments
  62. * @return null|object
  63. */
  64. protected function _processSpecialCases($className, $arguments)
  65. {
  66. $object = null;
  67. $interfaces = class_implements($className);
  68. if (in_array(\Magento\Framework\ObjectManager\ContextInterface::class, $interfaces)) {
  69. $object = $this->getObject($className, $arguments);
  70. } elseif (isset($this->_specialCases[$className])) {
  71. $method = $this->_specialCases[$className];
  72. $object = $this->{$method}($className);
  73. }
  74. return $object;
  75. }
  76. /**
  77. * Retrieve specific mock of core resource model
  78. *
  79. * @return \Magento\Framework\Module\ResourceInterface|\PHPUnit_Framework_MockObject_MockObject
  80. */
  81. protected function _getResourceModelMock()
  82. {
  83. $resourceMock = $this->_testObject->getMock(
  84. \Magento\Framework\Module\ModuleResource::class,
  85. ['getIdFieldName', '__sleep', '__wakeup'],
  86. [],
  87. '',
  88. false
  89. );
  90. $resourceMock->expects(
  91. $this->_testObject->any()
  92. )->method(
  93. 'getIdFieldName'
  94. )->will(
  95. $this->_testObject->returnValue('id')
  96. );
  97. return $resourceMock;
  98. }
  99. /**
  100. * Retrieve mock of core translator model
  101. *
  102. * @param string $className
  103. * @return \Magento\Framework\TranslateInterface|\PHPUnit_Framework_MockObject_MockObject
  104. */
  105. protected function _getTranslatorMock($className)
  106. {
  107. $translator = $this->_testObject->getMockBuilder($className)->disableOriginalConstructor()->getMock();
  108. $translateCallback = function ($arguments) {
  109. return is_array($arguments) ? vsprintf(array_shift($arguments), $arguments) : '';
  110. };
  111. $translator->expects(
  112. $this->_testObject->any()
  113. )->method(
  114. 'translate'
  115. )->will(
  116. $this->_testObject->returnCallback($translateCallback)
  117. );
  118. return $translator;
  119. }
  120. /**
  121. * Get mock without call of original constructor
  122. *
  123. * @param string $className
  124. * @return \PHPUnit_Framework_MockObject_MockObject
  125. */
  126. protected function _getMockWithoutConstructorCall($className)
  127. {
  128. $class = new \ReflectionClass($className);
  129. $mock = null;
  130. if ($class->isAbstract()) {
  131. $mock = $this->_testObject->getMockForAbstractClass($className, [], '', false, false);
  132. } else {
  133. $mock = $this->_testObject->getMock($className, [], [], '', false, false);
  134. }
  135. return $mock;
  136. }
  137. /**
  138. * Get class instance
  139. *
  140. * @param string $className
  141. * @param array $arguments
  142. * @return object
  143. */
  144. public function getObject($className, array $arguments = [])
  145. {
  146. if (is_subclass_of($className, \Magento\Framework\Api\AbstractSimpleObjectBuilder::class)
  147. || is_subclass_of($className, \Magento\Framework\Api\Builder::class)
  148. ) {
  149. return $this->getBuilder($className, $arguments);
  150. }
  151. $constructArguments = $this->getConstructArguments($className, $arguments);
  152. $reflectionClass = new \ReflectionClass($className);
  153. $newObject = $reflectionClass->newInstanceArgs($constructArguments);
  154. foreach (array_diff_key($arguments, $constructArguments) as $key => $value) {
  155. $propertyReflectionClass = $reflectionClass;
  156. while ($propertyReflectionClass) {
  157. if ($propertyReflectionClass->hasProperty($key)) {
  158. $reflectionProperty = $propertyReflectionClass->getProperty($key);
  159. $reflectionProperty->setAccessible(true);
  160. $reflectionProperty->setValue($newObject, $value);
  161. break;
  162. }
  163. $propertyReflectionClass = $propertyReflectionClass->getParentClass();
  164. }
  165. }
  166. return $newObject;
  167. }
  168. /**
  169. * Get data object builder
  170. *
  171. * @param string $className
  172. * @param array $arguments
  173. * @return object
  174. */
  175. protected function getBuilder($className, array $arguments)
  176. {
  177. if (!isset($arguments['objectFactory'])) {
  178. $objectFactory =
  179. $this->_testObject->getMock(\Magento\Framework\Api\ObjectFactory::class, [], [], '', false);
  180. $objectFactory->expects($this->_testObject->any())
  181. ->method('populateWithArray')
  182. ->will($this->_testObject->returnSelf());
  183. $objectFactory->expects($this->_testObject->any())
  184. ->method('populate')
  185. ->will($this->_testObject->returnSelf());
  186. $objectFactory->expects($this->_testObject->any())
  187. ->method('create')
  188. ->will($this->_testObject->returnCallback(
  189. function ($className, $arguments) {
  190. $reflectionClass = new \ReflectionClass($className);
  191. $constructorMethod = $reflectionClass->getConstructor();
  192. $parameters = $constructorMethod->getParameters();
  193. $args = [];
  194. foreach ($parameters as $parameter) {
  195. $parameterName = $parameter->getName();
  196. if (isset($arguments[$parameterName])) {
  197. $args[] = $arguments[$parameterName];
  198. } else {
  199. if ($parameter->isArray()) {
  200. $args[] = [];
  201. } elseif ($parameter->allowsNull()) {
  202. $args[] = null;
  203. } else {
  204. $mock = $this->_getMockWithoutConstructorCall($parameter->getClass()->getName());
  205. $args[] = $mock;
  206. }
  207. }
  208. }
  209. return new $className(...array_values($args));
  210. }
  211. ));
  212. $arguments['objectFactory'] = $objectFactory;
  213. }
  214. return new $className(...array_values($this->getConstructArguments($className, $arguments)));
  215. }
  216. /**
  217. * Retrieve associative array of arguments that used for new object instance creation
  218. *
  219. * @param string $className
  220. * @param array $arguments
  221. * @return array
  222. */
  223. public function getConstructArguments($className, array $arguments = [])
  224. {
  225. $constructArguments = [];
  226. if (!method_exists($className, '__construct')) {
  227. return $constructArguments;
  228. }
  229. $method = new \ReflectionMethod($className, '__construct');
  230. foreach ($method->getParameters() as $parameter) {
  231. $parameterName = $parameter->getName();
  232. $argClassName = null;
  233. $defaultValue = null;
  234. if (array_key_exists($parameterName, $arguments)) {
  235. $constructArguments[$parameterName] = $arguments[$parameterName];
  236. continue;
  237. }
  238. if ($parameter->isDefaultValueAvailable()) {
  239. $defaultValue = $parameter->getDefaultValue();
  240. }
  241. try {
  242. if ($parameter->getClass()) {
  243. $argClassName = $parameter->getClass()->getName();
  244. }
  245. $object = $this->_getMockObject($argClassName, $arguments);
  246. } catch (\ReflectionException $e) {
  247. $parameterString = $parameter->__toString();
  248. $firstPosition = strpos($parameterString, '<required>');
  249. if ($firstPosition !== false) {
  250. $parameterString = substr($parameterString, $firstPosition + 11);
  251. $parameterString = substr($parameterString, 0, strpos($parameterString, ' '));
  252. $object = $this->_testObject->getMock($parameterString, [], [], '', false);
  253. }
  254. }
  255. $constructArguments[$parameterName] = null === $object ? $defaultValue : $object;
  256. }
  257. return $constructArguments;
  258. }
  259. /**
  260. * Get collection mock
  261. *
  262. * @param string $className
  263. * @param array $data
  264. * @return \PHPUnit_Framework_MockObject_MockObject
  265. * @throws \InvalidArgumentException
  266. */
  267. public function getCollectionMock($className, array $data)
  268. {
  269. if (!is_subclass_of($className, \Magento\Framework\Data\Collection::class)) {
  270. throw new \InvalidArgumentException(
  271. $className . ' does not instance of \Magento\Framework\Data\Collection'
  272. );
  273. }
  274. $mock = $this->_testObject->getMock($className, [], [], '', false, false);
  275. $iterator = new \ArrayIterator($data);
  276. $mock->expects(
  277. $this->_testObject->any()
  278. )->method(
  279. 'getIterator'
  280. )->will(
  281. $this->_testObject->returnValue($iterator)
  282. );
  283. return $mock;
  284. }
  285. /**
  286. * Helper function that creates a mock object for a given class name.
  287. *
  288. * Will return a real object in some cases to assist in testing.
  289. *
  290. * @param string $argClassName
  291. * @param array $arguments
  292. * @return null|object|\PHPUnit_Framework_MockObject_MockObject
  293. */
  294. private function _getMockObject($argClassName, array $arguments)
  295. {
  296. if (is_subclass_of($argClassName, \Magento\Framework\Api\ExtensibleObjectBuilder::class)) {
  297. $object = $this->getBuilder($argClassName, $arguments);
  298. return $object;
  299. } else {
  300. $object = $this->_createArgumentMock($argClassName, $arguments);
  301. return $object;
  302. }
  303. }
  304. /**
  305. * Set mocked property
  306. *
  307. * @param object $object
  308. * @param string $propertyName
  309. * @param object $propertyValue
  310. * @return void
  311. */
  312. public function setBackwardCompatibleProperty($object, $propertyName, $propertyValue)
  313. {
  314. $reflection = new \ReflectionClass(get_class($object));
  315. $reflectionProperty = $reflection->getProperty($propertyName);
  316. $reflectionProperty->setAccessible(true);
  317. $reflectionProperty->setValue($object, $propertyValue);
  318. }
  319. }