[TASK] Add possibility creating accessible mock for abstract classes
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Tests / BaseTestCase.php
1 <?php
2 namespace TYPO3\CMS\Core\Tests;
3
4 /***************************************************************
5 * Copyright notice
6 *
7 * (c) 2005-2013 Robert Lemke (robert@typo3.org)
8 * All rights reserved
9 *
10 * This script is part of the TYPO3 project. The TYPO3 project is
11 * free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * The GNU General Public License can be found at
17 * http://www.gnu.org/copyleft/gpl.html.
18 *
19 * This script is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * This copyright notice MUST APPEAR in all copies of the script!
25 ***************************************************************/
26
27 /**
28 * The mother of all test cases.
29 *
30 * Don't sub class this test case but rather choose a more specialized base test case,
31 * such as UnitTestCase or FunctionalTestCase
32 *
33 */
34 abstract class BaseTestCase extends \PHPUnit_Framework_TestCase {
35
36 /**
37 * Whether global variables should be backed up
38 *
39 * @var boolean
40 */
41 protected $backupGlobals = TRUE;
42
43 /**
44 * Whether static attributes should be backed up
45 *
46 * @var boolean
47 */
48 protected $backupStaticAttributes = FALSE;
49
50 /**
51 * Creates a mock object which allows for calling protected methods and access of protected properties.
52 *
53 * @param string $originalClassName name of class to create the mock object of, must not be empty
54 * @param array<string> $methods name of the methods to mock
55 * @param array $arguments arguments to pass to constructor
56 * @param string $mockClassName the class name to use for the mock class
57 * @param boolean $callOriginalConstructor whether to call the constructor
58 * @param boolean $callOriginalClone whether to call the __clone method
59 * @param boolean $callAutoload whether to call any autoload function
60 *
61 * @return \PHPUnit_Framework_MockObject_MockObject|AccessibleObjectInterface
62 * a mock of $originalClassName with access methods added
63 *
64 * @see \TYPO3\CMS\Extbase\Tests\Unit\BaseTestCase::getAccessibleMock
65 */
66 protected function getAccessibleMock(
67 $originalClassName, array $methods = array(), array $arguments = array(), $mockClassName = '',
68 $callOriginalConstructor = TRUE, $callOriginalClone = TRUE, $callAutoload = TRUE
69 ) {
70 if ($originalClassName === '') {
71 throw new \InvalidArgumentException('$originalClassName must not be empty.', 1334701880);
72 }
73
74 return $this->getMock(
75 $this->buildAccessibleProxy($originalClassName),
76 $methods,
77 $arguments,
78 $mockClassName,
79 $callOriginalConstructor,
80 $callOriginalClone,
81 $callAutoload
82 );
83 }
84
85 /**
86 * Returns a mock object which allows for calling protected methods and access
87 * of protected properties.
88 *
89 * @param string $originalClassName Full qualified name of the original class
90 * @param array $arguments
91 * @param string $mockClassName
92 * @param boolean $callOriginalConstructor
93 * @param boolean $callOriginalClone
94 * @param boolean $callAutoload
95 *
96 * @throws \InvalidArgumentException
97 *
98 * @return \PHPUnit_Framework_MockObject_MockObject|AccessibleObjectInterface
99 *
100 */
101 protected function getAccessibleMockForAbstractClass(
102 $originalClassName, array $arguments = array(), $mockClassName = '',
103 $callOriginalConstructor = TRUE, $callOriginalClone = TRUE, $callAutoload = TRUE
104 ) {
105 if ($originalClassName === '') {
106 throw new \InvalidArgumentException('$originalClassName must not be empty.', 1384268260);
107 }
108
109 return $this->getMockForAbstractClass(
110 $this->buildAccessibleProxy($originalClassName),
111 $arguments,
112 $mockClassName,
113 $callOriginalConstructor,
114 $callOriginalClone,
115 $callAutoload
116 );
117 }
118
119 /**
120 * Creates a proxy class of the specified class which allows
121 * for calling even protected methods and access of protected properties.
122 *
123 * @param string $className Name of class to make available, must not be empty
124 *
125 * @return string Fully qualified name of the built class, will not be empty
126 *
127 * @see Tx_Extbase_Tests_Unit_BaseTestCase::buildAccessibleProxy
128 */
129 protected function buildAccessibleProxy($className) {
130 $accessibleClassName = uniqid('Tx_Phpunit_AccessibleProxy');
131 $class = new \ReflectionClass($className);
132 $abstractModifier = $class->isAbstract() ? 'abstract ' : '';
133
134 eval(
135 $abstractModifier . 'class ' . $accessibleClassName .
136 ' extends ' . $className . ' implements \TYPO3\CMS\Core\Tests\AccessibleObjectInterface {' .
137 'public function _call($methodName) {' .
138 'if ($methodName === \'\') {' .
139 'throw new \InvalidArgumentException(\'$methodName must not be empty.\', 1334663993);' .
140 '}' .
141 '$args = func_get_args();' .
142 'return call_user_func_array(array($this, $methodName), array_slice($args, 1));' .
143 '}' .
144 'public function _callRef(' .
145 '$methodName, &$arg1 = NULL, &$arg2 = NULL, &$arg3 = NULL, &$arg4 = NULL, &$arg5= NULL, &$arg6 = NULL, ' .
146 '&$arg7 = NULL, &$arg8 = NULL, &$arg9 = NULL' .
147 ') {' .
148 'if ($methodName === \'\') {' .
149 'throw new \InvalidArgumentException(\'$methodName must not be empty.\', 1334664210);' .
150 '}' .
151 'switch (func_num_args()) {' .
152 'case 0:' .
153 'throw new RuntimeException(\'The case of 0 arguments is not supposed to happen.\', 1334703124);' .
154 'break;' .
155 'case 1:' .
156 '$returnValue = $this->$methodName();' .
157 'break;' .
158 'case 2:' .
159 '$returnValue = $this->$methodName($arg1);' .
160 'break;' .
161 'case 3:' .
162 '$returnValue = $this->$methodName($arg1, $arg2);' .
163 'break;' .
164 'case 4:' .
165 '$returnValue = $this->$methodName($arg1, $arg2, $arg3);' .
166 'break;' .
167 'case 5:' .
168 '$returnValue = $this->$methodName($arg1, $arg2, $arg3, $arg4);' .
169 'break;' .
170 'case 6:' .
171 '$returnValue = $this->$methodName($arg1, $arg2, $arg3, $arg4, $arg5);' .
172 'break;' .
173 'case 7:' .
174 '$returnValue = $this->$methodName($arg1, $arg2, $arg3, $arg4, $arg5, $arg6);' .
175 'break;' .
176 'case 8:' .
177 '$returnValue = $this->$methodName($arg1, $arg2, $arg3, $arg4, $arg5, $arg6, $arg7);' .
178 'break;' .
179 'case 9:' .
180 '$returnValue = $this->$methodName($arg1, $arg2, $arg3, $arg4, $arg5, $arg6, $arg7, $arg8);' .
181 'break;' .
182 'case 10:' .
183 '$returnValue = $this->$methodName(' .
184 '$arg1, $arg2, $arg3, $arg4, $arg5, $arg6, $arg7, $arg8, $arg9' .
185 ');' .
186 'break;' .
187 'default:' .
188 'throw new \InvalidArgumentException(' .
189 '\'_callRef currently only allows calls to methods with no more than 9 parameters.\'' .
190 ');' .
191 '}' .
192 'return $returnValue;' .
193 '}' .
194 'public function _set($propertyName, $value) {' .
195 'if ($propertyName === \'\') {' .
196 'throw new \InvalidArgumentException(\'$propertyName must not be empty.\', 1334664355);' .
197 '}' .
198 '$this->$propertyName = $value;' .
199 '}' .
200 'public function _setRef($propertyName, &$value) {' .
201 'if ($propertyName === \'\') {' .
202 'throw new \InvalidArgumentException(\'$propertyName must not be empty.\', 1334664545);' .
203 '}' .
204 '$this->$propertyName = $value;' .
205 '}' .
206 'public function _setStatic($propertyName, $value) {' .
207 'if ($propertyName === \'\') {' .
208 'throw new \InvalidArgumentException(\'$propertyName must not be empty.\', 1344242602);' .
209 '}' .
210 'self::$$propertyName = $value;' .
211 '}' .
212 'public function _get($propertyName) {' .
213 'if ($propertyName === \'\') {' .
214 'throw new \InvalidArgumentException(\'$propertyName must not be empty.\', 1334664967);' .
215 '}' .
216 'return $this->$propertyName;' .
217 '}' .
218 'public function _getStatic($propertyName) {' .
219 'if ($propertyName === \'\') {' .
220 'throw new \InvalidArgumentException(\'$propertyName must not be empty.\', 1344242603);' .
221 '}' .
222 'return self::$$propertyName;' .
223 '}' .
224 '}'
225 );
226
227 return $accessibleClassName;
228 }
229
230 /**
231 * Helper function to call protected or private methods
232 *
233 * @param object $object The object to be invoked
234 * @param string $name the name of the method to call
235 * @return mixed
236 */
237 protected function callInaccessibleMethod($object, $name) {
238 // Remove first two arguments ($object and $name)
239 $arguments = func_get_args();
240 array_splice($arguments, 0, 2);
241
242 $reflectionObject = new \ReflectionObject($object);
243 $reflectionMethod = $reflectionObject->getMethod($name);
244 $reflectionMethod->setAccessible(TRUE);
245 return $reflectionMethod->invokeArgs($object, $arguments);
246 }
247 }
248 ?>