The module provides API to significantly simplify writing Drupal unit tests! Using the API reduces the amount of code in your unit tests to cover all the logic of tested functions, using provided stubs of Drupal services.

Basically, the module provides stubs for the most popular Drupal services like Entity Storage, EntityQuery, Database, Configuration Factory, and many others. The stubs can emulate the behavior of entities (create, load, save, delete) and core services, but without the real initialization of Drupal Kernel, database, filesystem, and other persistent storages, all are stored just in the memory.

Additionally, it provides utility functions to get and set private and protected properties and methods in classes, check database and entity query conditions, and many more.

And to use the Test Helpers API in your project or a contrib module, you don't even need to install it in Drupal, adding it via composer as a dev dependency (without installing on production) is enough:

composer require --dev drupal/test_helpers

And this approach will even work in Drupal.org infrastructure and for testing Drupal Core features too!

The module was presented at DrupalCon 2023 - here is the presentation and the slides.

An example:

A simple function that just renders a list of recent articles:

  public function articlesList() {
    $amount = $this->configFactory->get('test_helpers_example.settings')
      ->get('articles_to_display') ?? 3;

    $articlesIds = $this->entityTypeManager->getStorage('node')->getQuery()
      ->accessCheck()
      ->condition('status', 1)
      ->condition('type', 'article')
      ->sort('created', 'DESC')
      ->range(0, $amount)
      ->execute();

    $articles = $this->entityTypeManager->getStorage('node')
      ->loadMultiple($articlesIds);

    $articlesList = [];
    foreach ($articles as $article) {
      $linkText = $this->t('@label (@date by @username)', [
        '@label' => $article->label(),
        '@date' => $this->dateFormatter->format($article->created->value),
        '@username' => $article->uid->entity->label(),
      ]);
      $articlesList[] = $article->toLink($linkText);
    }

    return [
      '#theme' => 'item_list',
      '#items' => $articlesList,
      '#cache' => ['tags' => ['node_list:article']],
    ];
  }

A unit test to cover all the logic of that function, using Test Helpers API:

  public function testArticlesList() {
    TestHelpers::service('config.factory')->stubSetConfig('test_helpers_example.settings', ['articles_to_display' => 1]);
    TestHelpers::service('date.formatter')->stubSetFormat('medium', 'Medium', 'd.m.Y');
    TestHelpers::saveEntity('user', ['name' => 'Alice']);
    TestHelpers::saveEntity('node', ['type' => 'article', 'title' => 'A1', 'status' => 1, 'uid' => 1, 'created' => 1672574400]);
    TestHelpers::saveEntity('node', ['type' => 'article', 'title' => 'A2', 'status' => 1, 'uid' => 1, 'created' => 1672660800]);
    TestHelpers::saveEntity('node', ['type' => 'page', 'title' => 'P1', 'status' => 1, 'uid' => 1, 'created' => 1672747200]);
    TestHelpers::saveEntity('node', ['type' => 'article', 'title' => 'A3', 'status' => 0, 'uid' => 1, 'created' => 1672833600]);

    $result = TestHelpers::createClass(TestHelpersExampleController::class)->articlesList();
    $this->assertCount(1, $result['#items']);
    $this->assertEquals('A2 (02.01.2023 by Alice)', $result['#items'][0]->getText());
    $this->assertContains('node_list:article', $result['#cache']['tags']);
  }

The modern test function has just 12 lines of code - even less than the code of the tested function (29 lines)! You can see the full source in the file TestHelpersExampleControllerModernResultTest.php.

And the classic approach requires 100 lines of code - more than 8 times more! Here it is: TestHelpersExampleControllerClassicTest.php. So, which one is easier to write and review? 😉

More examples you can find in the "Test Helpers Example" submodule here.

API documentation: https://project.pages.drupalcode.org/test_helpers/

And here is the list of modules, that already use the Test Helpers API:

You can look at their unit tests in addition to the current examples, to better understand how to use the API.

Supporting this Module

You can convey gratitude to me for the development of the module and motivate me to do more through these services:
GitHub sponsor me Coindrop.to me Buy Me a Coffee

Supporting organizations: 
Tests this module on real projects

Project information

Releases