0

I am working on a contrib module with phpunit tests.

Currently there is no composer.json or phpunit.xml or installation routine in the module itself, instead one has to run the functional and browser tests from a custom drupal site, where the module is installed in web/modules/contrib/*. So far this works.

Now I want to use helper classes in tests/src/Helper/, in namespace "Drupal\\Tests\\$modulename\\Helper\\" and possibly elsewhere.

However, I noticed that drupal_phpunit_get_extension_namespaces() only registers some select namespaces within "Drupal\\Tests\\$modulename\\", instead of the entire namespace.

My questions:

  • Why does Drupal phpunit integration behave this way, instead of registering the entire tests/src/ namespace directory?
  • What is a good way to register the entire tests/src/ directory in the class loader? I still want to be able to run the tests from Drupal installation outside the module itself.

EDIT: New issue on drupal.org: #3258817: Why don't we register all of /tests/src/ for class loading?

0

I will provide a dirty workaround here, that can be easily copied without adjusting any code.

There might be better solutions, I am open for other answers!

Also, I won't promise 100% that this works in all circumstances. Perhaps opcache can bring nasty surprises?

Create the following file in "tests/src/$modulename/Traits/AutoloadHelperTrait.php". Replace <MODULENAME> with the real module name.

<?php

/**
 * @file
 * Registers the entire 'tests/src/' directory in the class loader.
 *
 * By default, Drupal phpunit integration registers only _some_ namespace
 * directories within 'tests/src/' in the class loader. One of them is
 * '/Traits/'. 
 * This workaround registers the top-level namespace.
 *
 * The code is written in a way that can easily be copied to other modules:
 * - It is independent of the module name.
 * - It is independent of where the module is placed in the filesystem.
 *
 * See https://drupal.stackexchange.com/questions/309143/autoload-helper-classes-in-tests-src-for-phpunit-in-contrib-module
 *
 * @see \drupal_phpunit_populate_class_loader()
 * @see \drupal_phpunit_get_extension_namespaces()
 */

declare(strict_types=1);

namespace Drupal\Tests\<MODULENAME>\Traits;

use Composer\Autoload\ClassLoader;

\call_user_func(static function (): void {
  // The Composer ClassLoader class is always present in the same location
  // within the vendor directory. From there we can work our way to the
  // autoload.php to get the real class loader. 
  $rc = new \ReflectionClass(ClassLoader::class);
  /** @var \Composer\Autoload\ClassLoader $classLoader */
  $classLoader = require dirname($rc->getFileName(), 2) . '/autoload.php';
  // Determine the namespace and directory from magic constants.
  // This allows the code to be copied elsewhere.
  $level = \substr_count(__NAMESPACE__, '\\');
  $dir = dirname(__DIR__, $level - 2);
  // The namespace always starts with 'Drupal\Tests\', independent of the
  // module name.
  $nspos = \strpos(__NAMESPACE__, '\\', 13);
  $namespace = \substr(__NAMESPACE__, 0, $nspos + 1);
  $classLoader->addPsr4($namespace, $dir);
});

/**
 * Include this trait to register the entire tests/src/ for class loading.
 */
trait AutoloadHelperTrait {}

Now include the trait in any test class that needs this autoloading enabled. Or include it from a test base class.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.