Test setup class loading issue prevents use of UserCreationTrait in Nightwatch test site scripts

Created on 21 July 2025, about 2 months ago

Problem/Motivation

When writing a test site setup script for Nightwatch that implements \Drupal\TestSite\TestSetupInterface, it is desirable to use traits such as \Drupal\Tests\user\Traits\UserCreationTrait to automate environment setup tasks (like user creation) in PHP rather than through the UI, to improve the speed and robustness of tests.

Currently, this is not possible because:

  • UserCreationTrait and similar traits in Drupal core require the test class to also extend or implement \PHPUnit\Framework\Assert.
  • If the class both implements TestSetupInterface and extends \PHPUnit\Framework\Assert, then the sanity check in \Drupal\TestSite\Commands\TestSiteInstallCommand::getSetupClass() fails. It checks the interface implementation on the wrong class, leading to a false negative and preventing the setup script from being used as intended.

Steps to reproduce

  1. Create a Nightwatch site setup PHP file where your class implements TestSetupInterface and uses UserCreationTrait.
  2. Attempt to execute the setup command via the Nightwatch CLI.
  3. Observe that the command fails with an error, even if the class implements the required interface, due to the interface check happening on the wrong class.

Relevant code

The problematic logic is in TestSiteInstallCommand::getSetupClass():

protected function getSetupClass($file) {
  // ...
  $class = array_pop($new_classes);
  if (!is_subclass_of($class, TestSetupInterface::class) && !is_subclass_of($class, TestPreinstallInterface::class)) {
    throw new \InvalidArgumentException("The class $class contained in $file needs to implement \Drupal\TestSite\TestSetupInterface or \Drupal\TestSite\TestPreinstallInterface");
  }
  return $class;
}

Currently, it uses array_pop($new_classes), so the last declared class in the file is picked, which may be the wrong one when traits or other supporting classes are present.

Proposed resolution

Change from:

$class = array_pop($new_classes);

To:

$class = array_shift($new_classes);

This would reliably select the main declared class (usually the first), ensuring interface checks operate as expected. This makes it possible to use traits like UserCreationTrait in setup scripts, enabling more efficient PHP-based environment setup.

Desired outcome

  • It becomes possible to use user and content creation traits in test setup scripts for Nightwatch and similar tools.
  • Avoids unnecessary UI-based setup, leading to faster, more reliable tests.

Additional information

  • This problem affects developers who want to leverage existing test traits in Drupal's core while setting up database state programmatically, and not through slow web-based UI automation.
  • Fixing this will simplify and speed up end-to-end and functional test suites.
๐Ÿ› Bug report
Status

Active

Version

11.0 ๐Ÿ”ฅ

Component

phpunit

Created by

๐Ÿ‡ญ๐Ÿ‡บHungary mxr576 Hungary

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Merge Requests

Comments & Activities

  • Issue created by @mxr576
  • Pipeline finished with Canceled
    about 2 months ago
    Total: 355s
    #552854
  • Pipeline finished with Failed
    about 2 months ago
    Total: 296s
    #552859
  • Pipeline finished with Success
    about 2 months ago
    Total: 516s
    #552871
  • ๐Ÿ‡ญ๐Ÿ‡บHungary mxr576 Hungary
  • ๐Ÿ‡ญ๐Ÿ‡บHungary mxr576 Hungary
  • ๐Ÿ‡ฆ๐Ÿ‡บAustralia mstrelan

    FWIW I came across similar issues in this commit, but ended up not using it. Might be worth a look. I feel like it might be a bit risky extending the phpunit Assert class.

  • ๐Ÿ‡ญ๐Ÿ‡บHungary mxr576 Hungary

    might be a bit risky extending the phpunit Assert class.

    Well, TestCase has a required constructor parameter, which conflicts with the way how these test scripts are initiated.

  • ๐Ÿ‡บ๐Ÿ‡ธUnited States smustgrave

    Is there currently a nightwatch test that needs this feature?

  • ๐Ÿ‡ญ๐Ÿ‡บHungary mxr576 Hungary

    This is a classic โ€œchicken and eggโ€ situation until this problem is resolved.

    Relying on the UI for arrange/setup tasks is high inefficient. It tends to make tests much more flaky and unstable, since browser-based operations are prone to random timeouts or failures. If a single step times out, the entire test could fail unnecessarily. For reliable and fast test execution, itโ€™s better to handle โ€œarrangeโ€ operations at the API or backend level whenever possible.

  • ๐Ÿ‡บ๐Ÿ‡ธUnited States smustgrave

    Why the RandomGeneratorTrait?

  • ๐Ÿ‡ญ๐Ÿ‡บHungary mxr576 Hungary

    Well, because the UserCreationTrait has many hidden dependency on RandomGeneratorTrait, so actually it should just use it and make the dependency explicit.

        elseif (!isset($values['name'])) {
          $edit['name'] = $this->randomMachineName();
        }

    (from \Drupal\Tests\user\Traits\UserCreationTrait::createUser())

  • ๐Ÿ‡ฎ๐Ÿ‡นItaly mondrake ๐Ÿ‡ฎ๐Ÿ‡น

    See comment inline

Production build 0.71.5 2024