diff --git a/core/modules/update/tests/src/Unit/UpdateFetcherTest.php b/core/modules/update/tests/src/Unit/UpdateFetcherTest.php index c3e447d1fcfbf98e8f96620da7c4a18b0a0f6c77..61c767deda673e99aff5f4785083e523f7d39168 100644 --- a/core/modules/update/tests/src/Unit/UpdateFetcherTest.php +++ b/core/modules/update/tests/src/Unit/UpdateFetcherTest.php @@ -28,7 +28,7 @@ class UpdateFetcherTest extends UnitTestCase { */ protected function setUp() { $config_factory = $this->getConfigFactoryStub(['update.settings' => ['fetch_url' => 'http://www.example.com']]); - $http_client_mock = $this->getMock('\GuzzleHttp\ClientInterface'); + $http_client_mock = $this->createMock('\GuzzleHttp\ClientInterface'); $this->updateFetcher = new UpdateFetcher($config_factory, $http_client_mock); } diff --git a/core/tests/Drupal/KernelTests/KernelTestBase.php b/core/tests/Drupal/KernelTests/KernelTestBase.php index 32c97b63b5552c28533a0101e4dd44f58d97e52e..ca1a517d6abcbfcae5c2201b7be5e73c76f1194b 100644 --- a/core/tests/Drupal/KernelTests/KernelTestBase.php +++ b/core/tests/Drupal/KernelTests/KernelTestBase.php @@ -20,6 +20,7 @@ use Drupal\simpletest\AssertContentTrait; use Drupal\Tests\AssertHelperTrait; use Drupal\Tests\ConfigTestTrait; +use Drupal\Tests\PhpunitCompatibilityTrait; use Drupal\Tests\RandomGeneratorTrait; use Drupal\Tests\TestRequirementsTrait; use Drupal\simpletest\TestServiceProvider; @@ -76,6 +77,7 @@ abstract class KernelTestBase extends TestCase implements ServiceProviderInterfa use RandomGeneratorTrait; use ConfigTestTrait; use TestRequirementsTrait; + use PhpunitCompatibilityTrait; /** * {@inheritdoc} diff --git a/core/tests/Drupal/Tests/BrowserTestBase.php b/core/tests/Drupal/Tests/BrowserTestBase.php index 404f3873b81f5ef56e50a2956d2a7e5c67c8fc73..240dc1bef3cf2a0a2f5936e418dfccb192e7eeb4 100644 --- a/core/tests/Drupal/Tests/BrowserTestBase.php +++ b/core/tests/Drupal/Tests/BrowserTestBase.php @@ -66,6 +66,7 @@ abstract class BrowserTestBase extends TestCase { createUser as drupalCreateUser; } use XdebugRequestTrait; + use PhpunitCompatibilityTrait; /** * The database prefix of this test run. diff --git a/core/tests/Drupal/Tests/PhpunitCompatibilityTrait.php b/core/tests/Drupal/Tests/PhpunitCompatibilityTrait.php new file mode 100644 index 0000000000000000000000000000000000000000..5cf020a8df6b999663a06aa624779f200eea6f46 --- /dev/null +++ b/core/tests/Drupal/Tests/PhpunitCompatibilityTrait.php @@ -0,0 +1,142 @@ +<?php + +namespace Drupal\Tests; + +/** + * Makes Drupal's test API forward compatible with multiple versions of PHPUnit. + */ +trait PhpunitCompatibilityTrait { + + /** + * Returns a mock object for the specified class using the available method. + * + * The getMock method does not exist in PHPUnit 6. To provide backward + * compatibility this trait provides the getMock method and uses createMock if + * this method is available on the parent class. + * + * @param string $originalClassName + * Name of the class to mock. + * @param array|null $methods + * When provided, only methods whose names are in the array are replaced + * with a configurable test double. The behavior of the other methods is not + * changed. Providing null means that no methods will be replaced. + * @param array $arguments + * Parameters to pass to the original class' constructor. + * @param string $mockClassName + * Class name for the generated test double class. + * @param bool $callOriginalConstructor + * Can be used to disable the call to the original class' constructor. + * @param bool $callOriginalClone + * Can be used to disable the call to the original class' clone constructor. + * @param bool $callAutoload + * Can be used to disable __autoload() during the generation of the test + * double class. + * @param bool $cloneArguments + * Enables the cloning of arguments passed to mocked methods. + * @param bool $callOriginalMethods + * Enables the invocation of the original methods. + * @param object $proxyTarget + * Sets the proxy target. + * + * @see \PHPUnit_Framework_TestCase::getMock + * @see https://github.com/sebastianbergmann/phpunit/wiki/Release-Announcement-for-PHPUnit-5.4.0 + * + * @return \PHPUnit_Framework_MockObject_MockObject + * + * @deprecated in Drupal 8.5.0 and will be removed before Drupal 9.0.0. + * Use \Drupal\Tests\PhpunitCompatibilityTrait::createMock() instead. + * + * @see https://www.drupal.org/node/2907725 + */ + public function getMock($originalClassName, $methods = array(), array $arguments = array(), $mockClassName = '', $callOriginalConstructor = TRUE, $callOriginalClone = TRUE, $callAutoload = TRUE, $cloneArguments = FALSE, $callOriginalMethods = FALSE, $proxyTarget = NULL) { + if (!$this->supports('getMock')) { + $mock = $this->getMockBuilder($originalClassName) + ->setMethods($methods) + ->setConstructorArgs($arguments) + ->setMockClassName($mockClassName) + ->setProxyTarget($proxyTarget); + if ($callOriginalConstructor) { + $mock->enableOriginalConstructor(); + } + else { + $mock->disableOriginalConstructor(); + } + if ($callOriginalClone) { + $mock->enableOriginalClone(); + } + else { + $mock->disableOriginalClone(); + } + if ($callAutoload) { + $mock->enableAutoload(); + } + else { + $mock->disableAutoload(); + } + if ($cloneArguments) { + $mock->enableArgumentCloning(); + } + else { + $mock->disableArgumentCloning(); + } + if ($callOriginalMethods) { + $mock->enableProxyingToOriginalMethods(); + } + else { + $mock->disableProxyingToOriginalMethods(); + } + return $mock->getMock(); + } + else { + return parent::getMock($originalClassName, $methods, $arguments, $mockClassName, $callOriginalConstructor, $callOriginalClone, $callAutoload, $cloneArguments, $callOriginalMethods, $proxyTarget); + } + } + + /** + * Returns a mock object for the specified class using the available method. + * + * The createMock method does not exist in PHPUnit 4. To provide forward + * compatibility this trait provides the createMock method and uses createMock + * if this method is available on the parent class or falls back to getMock if + * it isn't. + * + * @param string $originalClassName + * Name of the class to mock. + * + * @see \PHPUnit_Framework_TestCase::getMock + * + * @return \PHPUnit_Framework_MockObject_MockObject + */ + public function createMock($originalClassName) { + if ($this->supports('createMock')) { + return parent::createMock($originalClassName); + } + else { + return $this->getMock($originalClassName, [], [], '', FALSE, FALSE); + } + } + + /** + * Checks if the trait is used in a class that has a method. + * + * @param string $method + * Method to check. + * + * @return bool + * TRUE if the method is supported, FALSE if not. + */ + private function supports($method) { + // Get the parent class of the currently running test class. + $parent = get_parent_class($this); + // Ensure that the method_exists() check on the createMock method is carried + // out on the first parent of $this that does not have access to this + // trait's methods. This is because the trait also has a method called + // createMock(). Most often the check will be made on + // \PHPUnit\Framework\TestCase. + while (method_exists($parent, 'supports')) { + $parent = get_parent_class($parent); + } + return method_exists($parent, $method); + } + +} diff --git a/core/tests/Drupal/Tests/PhpunitCompatibilityTraitTest.php b/core/tests/Drupal/Tests/PhpunitCompatibilityTraitTest.php new file mode 100644 index 0000000000000000000000000000000000000000..145980bfe531c7ca752bcf00c629d2c67ad1f4c6 --- /dev/null +++ b/core/tests/Drupal/Tests/PhpunitCompatibilityTraitTest.php @@ -0,0 +1,115 @@ +<?php + +namespace Drupal\Tests; + +/** + * Tests the PHPUnit forward compatibility trait. + * + * @coversDefaultClass \Drupal\Tests\PhpunitCompatibilityTrait + * @group Tests + */ +class PhpunitCompatibilityTraitTest extends UnitTestCase { + + /** + * Tests that getMock is available and calls the correct parent method. + * + * @covers ::getMock + * @dataProvider providerMockVersions + */ + public function testGetMock($className, $expected) { + $class = new $className(); + $this->assertSame($expected, $class->getMock($this->randomMachineName())); + } + + /** + * Tests that createMock is available and calls the correct parent method. + * + * @covers ::createMock + * @dataProvider providerMockVersions + */ + public function testCreateMock($className, $expected) { + $class = new $className(); + $this->assertSame($expected, $class->createMock($this->randomMachineName())); + } + + /** + * Returns the class names and the string they return. + * + * @return array + */ + public function providerMockVersions() { + return [ + [UnitTestCasePhpunit4TestClass::class, 'PHPUnit 4'], + [UnitTestCasePhpunit4TestClassExtends::class, 'PHPUnit 4'], + [UnitTestCasePhpunit6TestClass::class, 'PHPUnit 6'], + [UnitTestCasePhpunit6TestClassExtends::class, 'PHPUnit 6'], + ]; + } + +} + +/** + * Test class for \PHPUnit\Framework\TestCase in PHPUnit 4. + */ +class Phpunit4TestClass { + public function getMock($originalClassName) { + return 'PHPUnit 4'; + } + +} + +/** + * Test class for \PHPUnit\Framework\TestCase in PHPUnit 6. + */ +class Phpunit6TestClass { + public function createMock($originalClassName) { + return 'PHPUnit 6'; + } + + public function getMockbuilder() { + return new Mockbuilder(); + } + +} + +/** + * Test double for PHPUnit_Framework_MockObject_MockBuilder. + */ +class Mockbuilder { + public function __call($name, $arguments) { + return $this; + } + + public function getMock() { + return 'PHPUnit 6'; + } + +} + +/** + * Test class for \Drupal\Tests\UnitTestCase with PHPUnit 4. + */ +class UnitTestCasePhpunit4TestClass extends Phpunit4TestClass { + use PhpunitCompatibilityTrait; + +} + +/** + * Test class for \Drupal\Tests\UnitTestCase with PHPUnit 4. + */ +class UnitTestCasePhpunit4TestClassExtends extends UnitTestCasePhpunit4TestClass { +} + +/** + * Test class for \Drupal\Tests\UnitTestCase with PHPUnit 6. + */ +class UnitTestCasePhpunit6TestClass extends Phpunit6TestClass { + use PhpunitCompatibilityTrait; + +} + +/** + * Test class for \Drupal\Tests\UnitTestCase with PHPUnit 6. + */ +class UnitTestCasePhpunit6TestClassExtends extends UnitTestCasePhpunit6TestClass { +} diff --git a/core/tests/Drupal/Tests/UnitTestCase.php b/core/tests/Drupal/Tests/UnitTestCase.php index b157274f3f0b584149c343f40b61619e3284672e..37012c5ed48759e8d1f73808faa83048bfa837ec 100644 --- a/core/tests/Drupal/Tests/UnitTestCase.php +++ b/core/tests/Drupal/Tests/UnitTestCase.php @@ -17,6 +17,8 @@ */ abstract class UnitTestCase extends TestCase { + use PhpunitCompatibilityTrait; + /** * The random generator. * @@ -135,7 +137,7 @@ public function getConfigFactoryStub(array $configs = []) { } // Construct a config factory with the array of configuration object stubs // as its return map. - $config_factory = $this->getMock('Drupal\Core\Config\ConfigFactoryInterface'); + $config_factory = $this->createMock('Drupal\Core\Config\ConfigFactoryInterface'); $config_factory->expects($this->any()) ->method('get') ->will($this->returnValueMap($config_get_map)); @@ -157,7 +159,7 @@ public function getConfigFactoryStub(array $configs = []) { * A mocked config storage. */ public function getConfigStorageStub(array $configs) { - $config_storage = $this->getMock('Drupal\Core\Config\NullStorage'); + $config_storage = $this->createMock('Drupal\Core\Config\NullStorage'); $config_storage->expects($this->any()) ->method('listAll') ->will($this->returnValue(array_keys($configs))); @@ -204,7 +206,7 @@ protected function getBlockMockWithMachineName($machine_name) { * A mock translation object. */ public function getStringTranslationStub() { - $translation = $this->getMock('Drupal\Core\StringTranslation\TranslationInterface'); + $translation = $this->createMock('Drupal\Core\StringTranslation\TranslationInterface'); $translation->expects($this->any()) ->method('translate') ->willReturnCallback(function ($string, array $args = [], array $options = []) use ($translation) { @@ -234,7 +236,7 @@ public function getStringTranslationStub() { * The container with the cache tags invalidator service. */ protected function getContainerWithCacheTagsInvalidator(CacheTagsInvalidatorInterface $cache_tags_validator) { - $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); + $container = $this->createMock('Symfony\Component\DependencyInjection\ContainerInterface'); $container->expects($this->any()) ->method('get') ->with('cache_tags.invalidator') @@ -251,7 +253,7 @@ protected function getContainerWithCacheTagsInvalidator(CacheTagsInvalidatorInte * The class resolver stub. */ protected function getClassResolverStub() { - $class_resolver = $this->getMock('Drupal\Core\DependencyInjection\ClassResolverInterface'); + $class_resolver = $this->createMock('Drupal\Core\DependencyInjection\ClassResolverInterface'); $class_resolver->expects($this->any()) ->method('getInstanceFromDefinition') ->will($this->returnCallback(function ($class) {