3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 * http://www.gnu.org/copyleft/gpl.html
22 use PHPUnit\Framework\TestCase
;
23 use PHPUnit\Framework\Exception
;
26 * Base class for unit tests.
28 * Extend this class if you are testing classes which use dependency injection and do not access
29 * global functions, variables, services or a storage backend.
33 abstract class MediaWikiUnitTestCase
extends TestCase
{
34 use PHPUnit4And6Compat
;
35 use MediaWikiCoversValidator
;
36 use MediaWikiTestCaseTrait
;
38 private static $originalGlobals;
39 private static $unitGlobals;
41 public static function setUpBeforeClass() {
42 parent
::setUpBeforeClass();
44 $reflection = new ReflectionClass( static::class );
45 $dirSeparator = DIRECTORY_SEPARATOR
;
46 if ( stripos( $reflection->getFilename(), "${dirSeparator}unit${dirSeparator}" ) === false ) {
47 self
::fail( 'This unit test needs to be in "tests/phpunit/unit"!' );
50 if ( defined( 'HHVM_VERSION' ) ) {
51 // There are a number of issues we encountered in trying to make this
52 // work on HHVM. Specifically, once an MediaWikiIntegrationTestCase executes
53 // before us, the original globals go missing. This might have to do with
54 // one of the non-unit tests passing GLOBALS somewhere and causing HHVM
55 // to get confused somehow.
59 self
::$unitGlobals =& TestSetup
::$bootstrapGlobals;
60 // The autoloader may change between bootstrap and the first test,
61 // so (lazily) capture these here instead.
62 self
::$unitGlobals['wgAutoloadClasses'] =& $GLOBALS['wgAutoloadClasses'];
63 self
::$unitGlobals['wgAutoloadLocalClasses'] =& $GLOBALS['wgAutoloadLocalClasses'];
64 // This value should always be true.
65 self
::$unitGlobals['wgAutoloadAttemptLowercase'] = true;
67 // Would be nice if we coud simply replace $GLOBALS as a whole,
68 // but unsetting or re-assigning that breaks the reference of this magic
69 // variable. Thus we have to modify it in place.
70 self
::$originalGlobals = [];
71 foreach ( $GLOBALS as $key => $_ ) {
72 // Stash current values
73 self
::$originalGlobals[$key] =& $GLOBALS[$key];
75 // Remove globals not part of the snapshot (see bootstrap.php, phpunit.php).
76 // Support: HHVM (avoid self-ref)
77 if ( $key !== 'GLOBALS' && !array_key_exists( $key, self
::$unitGlobals ) ) {
78 unset( $GLOBALS[$key] );
81 // Restore values from the early snapshot
82 // Not by ref because tests must not be able to modify the snapshot.
83 foreach ( self
::$unitGlobals as $key => $value ) {
84 $GLOBALS[ $key ] = $value;
91 protected function runTest() {
93 return parent
::runTest();
94 } catch ( ConfigException
$exception ) {
96 'Config variables must be mocked, they cannot be accessed directly in tests which extend '
98 $exception->getCode(),
104 protected function tearDown() {
105 if ( !defined( 'HHVM_VERSION' ) ) {
106 // Quick reset between tests
107 foreach ( $GLOBALS as $key => $_ ) {
108 if ( $key !== 'GLOBALS' && !array_key_exists( $key, self
::$unitGlobals ) ) {
109 unset( $GLOBALS[$key] );
112 foreach ( self
::$unitGlobals as $key => $value ) {
113 $GLOBALS[ $key ] = $value;
120 public static function tearDownAfterClass() {
121 if ( !defined( 'HHVM_VERSION' ) ) {
122 // Remove globals created by the test
123 foreach ( $GLOBALS as $key => $_ ) {
124 if ( $key !== 'GLOBALS' && !array_key_exists( $key, self
::$originalGlobals ) ) {
125 unset( $GLOBALS[$key] );
128 // Restore values (including reference!)
129 foreach ( self
::$originalGlobals as $key => &$value ) {
130 $GLOBALS[ $key ] =& $value;
134 parent
::tearDownAfterClass();
138 * Create a temporary hook handler which will be reset by tearDown.
139 * This replaces other handlers for the same hook.
140 * @param string $hookName Hook name
141 * @param mixed $handler Value suitable for a hook handler
144 protected function setTemporaryHook( $hookName, $handler ) {
145 // This will be reset by tearDown() when it restores globals. We don't want to use
146 // Hooks::register()/clear() because they won't replace other handlers for the same hook,
147 // which doesn't match behavior of MediaWikiIntegrationTestCase.
149 $wgHooks[$hookName] = [ $handler ];
152 protected function getMockMessage( $text, ...$params ) {
153 if ( isset( $params[0] ) && is_array( $params[0] ) ) {
154 $params = $params[0];
157 $msg = $this->getMockBuilder( Message
::class )
158 ->disableOriginalConstructor()
162 $msg->method( 'toString' )->willReturn( $text );
163 $msg->method( '__toString' )->willReturn( $text );
164 $msg->method( 'text' )->willReturn( $text );
165 $msg->method( 'parse' )->willReturn( $text );
166 $msg->method( 'plain' )->willReturn( $text );
167 $msg->method( 'parseAsBlock' )->willReturn( $text );
168 $msg->method( 'escaped' )->willReturn( $text );
170 $msg->method( 'title' )->willReturn( $msg );
171 $msg->method( 'inLanguage' )->willReturn( $msg );
172 $msg->method( 'inContentLanguage' )->willReturn( $msg );
173 $msg->method( 'useDatabase' )->willReturn( $msg );
174 $msg->method( 'setContext' )->willReturn( $msg );
176 $msg->method( 'exists' )->willReturn( true );
177 $msg->method( 'content' )->willReturn( new MessageContent( $msg ) );