My team and I have noticed that the Drupal convention is to create a separate namespace for unit tests (as opposed to the namespace define by, lets say, the custom module we are testing).

If they shared a namespace then we could, for example, easily override a function like t() for our tests. I know there are lots of ways to handle things like the t() function, but this convention of creating a new name space eliminates this particular option. Can anyone speak to why? What is the benefit?

  • This question may not be specific to Drupal or PHP. I see this done in some C# projects as well, but there is also stackoverflow.com/questions/12117254/…
    – mradcliffe
    Nov 28 '16 at 20:11
  • Will write up answer later, but a lot of it has to do with how tests are discovered and grouped into the various flavors and also how some tests share common functionality.
    – mpdonadio
    Nov 28 '16 at 20:30

There are few reasons why tests are in different namespaces.

Drupal have several different types of tests

  • WebTestBase (WTB), aka simpletests or integration tests
  • BrowsertestBase (BTB), integration tests (all WTB will eventually be replaced by equivalent BTB)
  • KernelTestBase (KTB), a barely bootstrapped version of Drupal, not an integration test but a lot more than a true unit test
  • JavascriptTestBase (JSTB), an integration test that is Javascript aware
  • UnitTestCase and PHPUnit_Framework_TestCase, unit tests

Note that integration are also called functional tests. I am going to ignore the difference between UnitTestCase and PHPUnit_Framework_TestCase right now and lump them together as unit tests.

The five different test types are all in different namespaces so the test discovery process can find them and place them into five groups. The test runner allows the various test groups to be run together, and also invoke them properly during runs.

For example, if making a big change, you can run all unit tests locally as a sanity check (running all of the take about a minute on a normalish local machine).

Tests associated with a particular module are also in the modules namespace, so you can easily run all tests for that module, and they won't be discovered if that module is disabled. For example, integration tests take along time (usually between 30 seconds and 4 minutes each). You can just run all of the Field UI tests at ones if you are doing work there.

The test runner also allows you to run all of the tests in a particular directory. This allows you to target a particular core subsystem that isn't in a module. For example, you can run all of the database kernel tests at once this way.

Some subsystems also define their own test bases. For example, the migration system defines its own test bases to centralize common functionality and to to have a common setup.

Tests also make frequent use of traits to share code between different types of tests.

The particular case you mention is an oddity. In a perfect world, the t() function wouldn't be used in object oriented code; the injected translation service would be used which could then be mocked in unit tests instead of needing to use shenanigans like shadowed globals.


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.