Commit 5f3b66ed authored by catch's avatar catch

Issue #2728579 by Mile23, neclimdul, Munavijayalakshmi, klausi, dawehner:...

Issue #2728579 by Mile23, neclimdul, Munavijayalakshmi, klausi, dawehner: Explicitly skip @requires module in PHPUnit Kernel and Browser tests
parent c9b30844
......@@ -189,14 +189,17 @@ public function getTestClasses($extension = NULL, array $types = []) {
// abstract class, trait or test fixture.
continue;
}
// Skip this test class if it requires unavailable modules.
// @todo PHPUnit skips tests with unmet requirements when executing a test
// (instead of excluding them upfront). Refactor test runner to follow
// that approach.
// Skip this test class if it is a Simpletest-based test and requires
// unavailable modules. TestDiscovery should not filter out module
// requirements for PHPUnit-based test classes.
// @todo Move this behavior to \Drupal\simpletest\TestBase so tests can be
// marked as skipped, instead.
// @see https://www.drupal.org/node/1273478
if (!empty($info['requires']['module'])) {
if (array_diff($info['requires']['module'], $this->availableExtensions['module'])) {
continue;
if ($info['type'] == 'Simpletest') {
if (!empty($info['requires']['module'])) {
if (array_diff($info['requires']['module'], $this->availableExtensions['module'])) {
continue;
}
}
}
......
<?php
namespace Drupal\simpletest\Tests;
use Drupal\simpletest\WebTestBase;
/**
* Tests if Simpletest-based tests are skipped based on module requirements.
*
* This test should always be skipped when TestDiscovery is used to discover it.
* This means that if you specify this test to run-tests.sh with --class or
* --file, this test will run and fail.
*
* @dependencies module_does_not_exist
*
* @group simpletest
*
* @todo Change or remove this test when Simpletest-based tests are able to skip
* themselves based on requirements.
* @see https://www.drupal.org/node/1273478
*/
class SkipRequiredModulesTest extends WebTestBase {
public function testModuleNotFound() {
$this->fail('This test should have been skipped during discovery.');
}
}
<?php
namespace Drupal\Tests\simpletest\Functional;
use Drupal\Tests\BrowserTestBase;
/**
* This test should not load since it requires a module that is not found.
*
* @group simpletest
* @dependencies simpletest_missing_module
*/
class MissingDependentModuleUnitTest extends BrowserTestBase {
/**
* Ensure that this test will not be loaded despite its dependency.
*/
public function testFail() {
$this->fail('Running test with missing required module.');
}
}
<?php
namespace Drupal\KernelTests\Core\Test;
use Drupal\FunctionalTests\BrowserMissingDependentModuleMethodTest;
use Drupal\FunctionalTests\BrowserMissingDependentModuleTest;
use Drupal\KernelTests\KernelTestBase;
/**
* @group Test
* @group FunctionalTests
*
* @coversDefaultClass \Drupal\Tests\BrowserTestBase
*/
class BrowserTestBaseTest extends KernelTestBase {
/**
* Tests that a test method is skipped when it requires a module not present.
*
* In order to catch checkRequirements() regressions, we have to make a new
* test object and run checkRequirements() here.
*
* @covers ::checkRequirements
* @covers ::checkModuleRequirements
*/
public function testMethodRequiresModule() {
require __DIR__ . '/../../../../fixtures/BrowserMissingDependentModuleMethodTest.php';
$stub_test = new BrowserMissingDependentModuleMethodTest();
// We have to setName() to the method name we're concerned with.
$stub_test->setName('testRequiresModule');
// We cannot use $this->setExpectedException() because PHPUnit would skip
// the test before comparing the exception type.
try {
$stub_test->publicCheckRequirements();
$this->fail('Missing required module throws skipped test exception.');
}
catch (\PHPUnit_Framework_SkippedTestError $e) {
$this->assertEqual('Required modules: module_does_not_exist', $e->getMessage());
}
}
/**
* Tests that a test case is skipped when it requires a module not present.
*
* In order to catch checkRequirements() regressions, we have to make a new
* test object and run checkRequirements() here.
*
* @covers ::checkRequirements
* @covers ::checkModuleRequirements
*/
public function testRequiresModule() {
require __DIR__ . '/../../../../fixtures/BrowserMissingDependentModuleTest.php';
$stub_test = new BrowserMissingDependentModuleTest();
// We have to setName() to the method name we're concerned with.
$stub_test->setName('testRequiresModule');
// We cannot use $this->setExpectedException() because PHPUnit would skip
// the test before comparing the exception type.
try {
$stub_test->publicCheckRequirements();
$this->fail('Missing required module throws skipped test exception.');
}
catch (\PHPUnit_Framework_SkippedTestError $e) {
$this->assertEqual('Required modules: module_does_not_exist', $e->getMessage());
}
}
}
......@@ -21,6 +21,7 @@
use Drupal\Tests\AssertHelperTrait;
use Drupal\Tests\ConfigTestTrait;
use Drupal\Tests\RandomGeneratorTrait;
use Drupal\Tests\TestRequirementsTrait;
use Drupal\simpletest\TestServiceProvider;
use PHPUnit\Framework\TestCase;
use Symfony\Component\DependencyInjection\Reference;
......@@ -46,9 +47,6 @@
*
* @see \Drupal\Tests\KernelTestBase::$modules
* @see \Drupal\Tests\KernelTestBase::enableModules()
*
* @todo Extend ::setRequirementsFromAnnotation() and ::checkRequirements() to
* account for '@requires module'.
*/
abstract class KernelTestBase extends TestCase implements ServiceProviderInterface {
......@@ -57,6 +55,7 @@ abstract class KernelTestBase extends TestCase implements ServiceProviderInterfa
use AssertHelperTrait;
use RandomGeneratorTrait;
use ConfigTestTrait;
use TestRequirementsTrait;
/**
* {@inheritdoc}
......@@ -212,15 +211,6 @@ public static function setUpBeforeClass() {
chdir(static::getDrupalRoot());
}
/**
* Returns the drupal root directory.
*
* @return string
*/
protected static function getDrupalRoot() {
return dirname(dirname(substr(__DIR__, 0, -strlen(__NAMESPACE__))));
}
/**
* {@inheritdoc}
*/
......
......@@ -9,6 +9,7 @@
/**
* @coversDefaultClass \Drupal\KernelTests\KernelTestBase
*
* @group PHPUnit
* @group Test
* @group KernelTests
......@@ -222,6 +223,60 @@ public function testLocalTimeZone() {
$this->assertEquals('Australia/Sydney', date_default_timezone_get());
}
/**
* Tests that a test method is skipped when it requires a module not present.
*
* In order to catch checkRequirements() regressions, we have to make a new
* test object and run checkRequirements() here.
*
* @covers ::checkRequirements
* @covers ::checkModuleRequirements
*/
public function testMethodRequiresModule() {
require __DIR__ . '/../../fixtures/KernelMissingDependentModuleMethodTest.php';
$stub_test = new KernelMissingDependentModuleMethodTest();
// We have to setName() to the method name we're concerned with.
$stub_test->setName('testRequiresModule');
// We cannot use $this->setExpectedException() because PHPUnit would skip
// the test before comparing the exception type.
try {
$stub_test->publicCheckRequirements();
$this->fail('Missing required module throws skipped test exception.');
}
catch (\PHPUnit_Framework_SkippedTestError $e) {
$this->assertEqual('Required modules: module_does_not_exist', $e->getMessage());
}
}
/**
* Tests that a test case is skipped when it requires a module not present.
*
* In order to catch checkRequirements() regressions, we have to make a new
* test object and run checkRequirements() here.
*
* @covers ::checkRequirements
* @covers ::checkModuleRequirements
*/
public function testRequiresModule() {
require __DIR__ . '/../../fixtures/KernelMissingDependentModuleTest.php';
$stub_test = new KernelMissingDependentModuleTest();
// We have to setName() to the method name we're concerned with.
$stub_test->setName('testRequiresModule');
// We cannot use $this->setExpectedException() because PHPUnit would skip
// the test before comparing the exception type.
try {
$stub_test->publicCheckRequirements();
$this->fail('Missing required module throws skipped test exception.');
}
catch (\PHPUnit_Framework_SkippedTestError $e) {
$this->assertEqual('Required modules: module_does_not_exist', $e->getMessage());
}
}
/**
* {@inheritdoc}
*/
......
......@@ -56,6 +56,7 @@ abstract class BrowserTestBase extends TestCase {
createContentType as drupalCreateContentType;
}
use ConfigTestTrait;
use TestRequirementsTrait;
use UserCreationTrait {
createRole as drupalCreateRole;
createUser as drupalCreateUser;
......
<?php
namespace Drupal\Tests;
use Drupal\Core\Extension\ExtensionDiscovery;
/**
* Allows test classes to require Drupal modules as dependencies.
*
* This trait is assumed to be on a subclass of \PHPUnit_Framework_TestCase, and
* overrides \PHPUnit_Framework_TestCase::checkRequirements(). This allows the
* test to be marked as skipped before any kernel boot processes have happened.
*/
trait TestRequirementsTrait {
/**
* Returns the Drupal root directory.
*
* @return string
*/
protected static function getDrupalRoot() {
return dirname(dirname(substr(__DIR__, 0, -strlen(__NAMESPACE__))));
}
/**
* Check module requirements for the Drupal use case.
*
* This method is assumed to override
* \PHPUnit_Framework_TestCase::checkRequirements().
*
* @throws \PHPUnit_Framework_SkippedTestError
* Thrown when the requirements are not met, and this test should be
* skipped. Callers should not catch this exception.
*/
protected function checkRequirements() {
parent::checkRequirements();
$root = static::getDrupalRoot();
// Check if required dependencies exist.
$annotations = $this->getAnnotations();
if (!empty($annotations['class']['requires'])) {
$this->checkModuleRequirements($root, $annotations['class']['requires']);
}
if (!empty($annotations['method']['requires'])) {
$this->checkModuleRequirements($root, $annotations['method']['requires']);
}
}
/**
* Checks missing module requirements.
*
* Iterates through a list of requires annotations and looks for missing
* modules. The test will be skipped if any of the required modules is
* missing.
*
* @param string $root
* The path to the root of the Drupal installation to scan.
* @param string[] $annotations
* A list of requires annotations from either a method or class annotation.
*
* @throws \PHPUnit_Framework_SkippedTestError
* Thrown when the requirements are not met, and this test should be
* skipped. Callers should not catch this exception.
*/
private function checkModuleRequirements($root, array $annotations) {
// drupal_valid_ua() might not be loaded.
require_once $root . '/core/includes/bootstrap.inc';
// Make a list of required modules.
$required_modules = [];
foreach ($annotations as $requirement) {
if (strpos($requirement, 'module ') === 0) {
$required_modules[] = trim(str_replace('module ', '', $requirement));
}
}
// If there are required modules, check if they're available.
if (!empty($required_modules)) {
// Scan for modules.
$discovery = new ExtensionDiscovery($root, FALSE);
$discovery->setProfileDirectories([]);
$list = array_keys($discovery->scan('module'));
$not_available = array_diff($required_modules, $list);
if (!empty($not_available)) {
throw new \PHPUnit_Framework_SkippedTestError('Required modules: ' . implode(', ', $not_available));
}
}
}
}
<?php
namespace Drupal\FunctionalTests;
use Drupal\Tests\BrowserTestBase;
/**
* A fixture test class with requires annotation.
*
* This is a fixture class for
* \Drupal\FunctionalTests\BrowserTestBaseTest::testMethodRequiresModule().
*
* This test class should not be discovered by run-tests.sh or phpunit.
*
* @group fixture
*/
class BrowserMissingDependentModuleMethodTest extends BrowserTestBase {
/**
* This method should be skipped since it requires a module that is not found.
*
* @requires module module_does_not_exist
*/
public function testRequiresModule() {
$this->fail('Running test with missing required module.');
}
/**
* Public access for checkRequirements() to avoid reflection.
*/
public function publicCheckRequirements() {
return parent::checkRequirements();
}
}
<?php
namespace Drupal\FunctionalTests;
use Drupal\Tests\BrowserTestBase;
/**
* A fixture test class with requires annotation.
*
* This is a fixture class for
* \Drupal\FunctionalTests\BrowserTestBaseTest::testRequiresModule().
*
* This test class should not be discovered by run-tests.sh or phpunit.
*
* @requires module module_does_not_exist
* @group fixture
*/
class BrowserMissingDependentModuleTest extends BrowserTestBase {
/**
* Placeholder test method.
*
* Depending on configuration, PHPUnit might fail a test if it has no test
* methods, so we must provide one. This method should never be executed.
*/
public function testRequiresModule() {
$this->fail('Running test with missing required module.');
}
/**
* Public access for checkRequirements() to avoid reflection.
*/
public function publicCheckRequirements() {
return parent::checkRequirements();
}
}
<?php
namespace Drupal\KernelTests;
/**
* A fixture test class with requires annotation.
*
* This is a fixture class for
* \Drupal\KernelTests\KernelTestBaseTest::testRequiresModule().
*
* This test class should not be discovered by run-tests.sh or phpunit.
*
* @group fixture
*/
class KernelMissingDependentModuleMethodTest extends KernelTestBase {
/**
* This method should be skipped since it requires a module that is not found.
*
* @requires module module_does_not_exist
*/
public function testRequiresModule() {
$this->fail('Running test with missing required module.');
}
/**
* Public access for checkRequirements() to avoid reflection.
*/
public function publicCheckRequirements() {
return parent::checkRequirements();
}
}
<?php
namespace Drupal\KernelTests;
/**
* A fixture test class with requires annotation.
*
* This is a fixture class for
* \Drupal\KernelTests\KernelTestBaseTest::testRequiresModule().
*
* This test class should not be discovered by run-tests.sh or phpunit.
*
* @requires module module_does_not_exist
* @group fixture
*/
class KernelMissingDependentModuleTest extends KernelTestBase {
/**
* Placeholder test method.
*
* Depending on configuration, PHPUnit might fail a test if it has no test
* methods, so we must provide one. This method should never be executed.
*/
public function testRequiresModule() {
$this->fail('Running test with missing required module.');
}
/**
* Public access for checkRequirements() to avoid reflection.
*/
public function publicCheckRequirements() {
return parent::checkRequirements();
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment