Use \Drupal consistently in tests

Created on 16 August 2013, over 11 years ago
Updated 25 July 2024, 10 months ago

Problem/Motivation

Kernel and functional tests should use the \Drupal discovery class even though $this->container is also available.

Proposed resolution

Replace all calls to $this->container->get() with \Drupal::service(), in core module kernel and functional test classes.

Use git grep '\Drupal::' core/modules/*/src/Tests/* to find these. Get a list of files this way: git grep --name-only '\Drupal::' core/modules/*/src/Tests/*

Note

Originally, this issue proposed the exact opposite. After a lot of discussion here and on 🌱 Use \Drupal consistently in tests Needs work , it was decided to use \Drupal::service() consistently.

Remaining tasks

file issue per module
fix issues

Related Issues

Initially from #1957142-64: Replace config() with Drupal::config()
#2001206: Replace drupal_container() with Drupal::service()

@todo list of issues
#2067007: Replace \Drupal:: with $this->container->get() in test classes of aggregator module
#2066999: Replace \Drupal:: with $this->container->get() in test classes of system module

Original report by @andypost

Problem/Motivation

SimpleTest TestBase-based tests should use the fixture container, rather than relying on the \Drupal discovery class.

Currently, TestBase assigns the fixture container to \Drupal, since a lot of existing code is still not properly injected.

As more of core is injected, it will be more important to use the fixture for tests, so that we can draw a line of isolation between the test runner and the system under test. This will allow for greater latitude in mocking services and filesystems for core.

Proposed resolution

Replace all calls to \Drupal:: with corresponding $this->container->get(), in core module test classes.
Use git grep '\Drupal::' core/modules/*/src/Tests/* to find these. Get a list of files this way: git grep --name-only '\Drupal::' core/modules/*/src/Tests/*

🌱 Plan
Status

Needs work

Version

11.0 🔥

Component
PHPUnit 

Last updated 1 day ago

Created by

🇫🇷France andypost

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

Merge Requests

Comments & Activities

Not all content is available!

It's likely this issue predates Contrib.social: some issue and comment data are missing.

  • 🇩🇪Germany Anybody Porta Westfalica
  • First commit to issue fork.
  • Merge request !11707PHPStan rule to enforce \Drupal::service → (Open) created by mstrelan
  • Pipeline finished with Failed
    about 1 month ago
    Total: 158s
    #462987
  • 🇦🇺Australia mstrelan

    The IS mentions both Kernel and Functional tests should use \Drupal::service, but according to alexpott in #2699565-27: Replace \Drupal:: with $this->container->get() in test classes that allow dependency injection of Basic Auth module this is only true for Functional tests.

    I've created an MR with a phpstan rule to identify functional tests that access $this->container. See https://git.drupalcode.org/issue/drupal-2066993/-/jobs/4841841 for results. We could land this and add the existing violations to the baseline. Then we can open follow up issues to cleanup the baseline. I suspect it should be simple enough to write a rector rule to replace $this->container->get() with \Drupal::service().

  • 🇦🇺Australia acbramley

    Removing the original report here as it's just confusing as it was originally the opposite.

  • IIRC, using \Drupal::service() instead of $this->container->get() addresses issues that surfaced while trying to fix 📌 Memory leak in DrupalKernel when installing modules Active in any test that has a container rebuild. Some of the relevant discussion might've been in Slack, though.

  • Pipeline finished with Failed
    about 1 month ago
    Total: 178s
    #463045
  • Pipeline finished with Failed
    about 1 month ago
    Total: 588s
    #463047
  • Pipeline finished with Success
    about 1 month ago
    Total: 766s
    #463059
  • 🇦🇺Australia mstrelan

    Managed to fix 405 files and only had one fail. Have added a @phpstan-ignore to NodeAccessGrantsCacheContextTest for now. Changing from a Plan to a Task and setting Needs Review.

  • 🇮🇹Italy mondrake 🇮🇹

    Some comments.

    1. General topic: adding new PHPStan rules to Drupal core. As already found in 📌 Introduce a @return-type-will-be-added annotation to indicate return type addition in next major; enforce with a PHPStan rule Active , new Drupal-defined rules added to Drupal phpstan.neon will not be visible to custom/contrib unless they also add the rule to their projects' neon files. mglaman/phpstan-drupal in that sense is different because in that case all the rules are added as part of the extension installation via composer. Unless we do something, there's high risk that Drupal core own rules will not be checked. The existing rule is different because it only checks Drupal component code that is not present in modules/themes/etc.
    2. As discussed in Add a trait for autowiring properties in tests Needs work , it seems to me that only preventing usage of $this->container->get() is not enough. I understand the problem is that any service should always be accessed from the singleton. Therefore we should also discourage contructs that store a service in a test property or a local variable like
        $cache = \Drupal::service('cache');
        ...
        $cache->get(...);
      

      no? That would be tricky.

    3. The new PHPStan rule needs tests. Actually see how test coverage instruments in PHPStan rules testing are adding the red line in the MR where the rule is introduced.
  • 🇦🇺Australia mstrelan

    Thanks @mondrake, some great points there. My initial thoughts on those:

    1. I think automatically applying this rule everywhere in contrib and custom code would be quite disruptive, so it's almost a nice side effect that it would be opt-in. Ultimately I don't really care where it lives. It could probably even be a phpcs sniff that can be auto-fixed with phpcbf.

    2. I think that could be handled separately in a follow up? Better to make progress than to get it perfect first go. Unless doing this makes things worse than the current state.

    3. Absolutely. Keen to get more feedback first though.

  • 🇺🇸United States mile23 Seattle, WA

    "I understand the problem is that any service should always be accessed from the singleton."

    The problem is that the service discovery singleton should actually be a singleton, but Drupal doesn't do that. In theory it shouldn't matter whether we do \Drupal or $this->container, but it does matter, and that's bad.

    Since we allow rebuilding the container, and since we allow \Drupal, our code should ensure that any container rebuild ends up in either $this->container, or \Drupal. The test is: does that happen, and is it reflected in our kernel or functional fixture?

    Using only \Drupal as a rule means our test will reflect whether all container rebuilds are reflected in \Drupal, which is good. It also means that we never discover whether the test base class' $container property is updated to reflect the not-really-a-singleton, which might matter or might not. Imagine a BTB with $this->container removed, and whether that would be an accurate test of anything. In that world, you're forced to use \Drupal.

    Then... In BrowserTestBase, we launch the test into a separate process, which builds the container. In the case of autowiring, as @catch points out in that other issue, you have to be cognizant of which process is doing the autowiring. I think it's better to be explicit about what service lives where, at which point of the test, and so I'd say we should access services local to the test method, and not anywhere else. Basically don't allow \Drupal or $this->container in setup(), but that's really a style thing for me.

    So: I'd say if we're going to disallow $this->container, then we should rescope this issue to be to deprecate $this->container. That way there is no confusion about what's going on. If that seems too radical (even though it's essentially what we're doing here anyway), then we should re-evaluate the way Drupal allows rebuilding the service container out-of-bounds.

Production build 0.71.5 2024