Verified Commit f320141d authored by Lee Rowlands's avatar Lee Rowlands
Browse files

Issue #2795317 by hswong3i, alexpott, Lendude, bircher, dawehner, martin107,...

Issue #2795317 by hswong3i, alexpott, Lendude, bircher, dawehner, martin107, Jo Fitzgerald, mondrake: Allow PHPUnit 6+ support for object mocking
parent b5b3949b
Loading
Loading
Loading
Loading
+1 −1
Changes for core/modules/update/tests/src/Unit/UpdateFetcherTest.php: 1 added line, 1 removed line.
Original line number Diff line number Diff line
@@ -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);
  }

+2 −0
Changes for core/tests/Drupal/KernelTests/KernelTestBase.php: 2 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -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}
+1 −0
Changes for core/tests/Drupal/Tests/BrowserTestBase.php: 1 added line, 0 removed lines.
Original line number Diff line number Diff line
@@ -66,6 +66,7 @@ abstract class BrowserTestBase extends TestCase {
    createUser as drupalCreateUser;
  }
  use XdebugRequestTrait;
  use PhpunitCompatibilityTrait;

  /**
   * The database prefix of this test run.
+142 −0
Changes for core/tests/Drupal/Tests/PhpunitCompatibilityTrait.php: 142 added lines, 0 removed lines.
Original line number Diff line number Diff line
<?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);
  }

}
+115 −0
Changes for core/tests/Drupal/Tests/PhpunitCompatibilityTraitTest.php: 115 added lines, 0 removed lines.
Original line number Diff line number Diff line
<?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 {
}
Loading