Drupal is a registered trademark of Dries Buytaert
drupal 11.3.7 Update released for Drupal core (11.3.7)! drupal 11.2.11 Update released for Drupal core (11.2.11)! drupal 10.6.7 Update released for Drupal core (10.6.7)! drupal 10.5.9 Update released for Drupal core (10.5.9)! cms 2.1.1 Update released for Drupal core (2.1.1)! drupal 11.3.6 Update released for Drupal core (11.3.6)! drupal 10.6.6 Update released for Drupal core (10.6.6)! cms 2.1.0 Update released for Drupal core (2.1.0)! bootstrap 8.x-3.40 Minor update available for theme bootstrap (8.x-3.40). menu_link_attributes 8.x-1.7 Minor update available for module menu_link_attributes (8.x-1.7). eca 3.1.1 Minor update available for module eca (3.1.1). layout_paragraphs 2.1.3 Minor update available for module layout_paragraphs (2.1.3). ai 1.3.3 Minor update available for module ai (1.3.3). ai 1.2.14 Minor update available for module ai (1.2.14). node_revision_delete 2.0.3 Minor update available for module node_revision_delete (2.0.3). moderated_content_bulk_publish 2.0.52 Minor update available for module moderated_content_bulk_publish (2.0.52). klaro 3.0.10 Minor update available for module klaro (3.0.10). klaro 3.0.9 Minor update available for module klaro (3.0.9). layout_paragraphs 2.1.2 Minor update available for module layout_paragraphs (2.1.2). geofield_map 11.1.8 Minor update available for module geofield_map (11.1.8).

test_helpers

5 sites Security covered
View on drupal.org

The module provides API to simplify writing Drupal tests - unit and functional. Using the API can significantly reduce the amount of code in your tests to cover all the logic of tested functions, using provided stubs of Drupal core services and many other helpers.

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, a database, and other persistent storages, all are emulated in the memory.

Additionally, it provides some utility functions to get private properties and methods from classes, Plugin Definitions from a YAML file, and many more.

Also, it provides helpers for functional and Nightwatch tests, which can significantly speed up the test execution time.

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/

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. All projects that use Test Helpers ยป

Supporting this Module

You can convey gratitude to me for the development of the module and motivate me to do more through these services:

Activity

Total releases
8
First release
Apr 2025
Latest release
3 months ago
Release cadence
40 days
Stability
63% stable

Release Timeline

Releases

Version Type Release date
1.7.0 Stable Jan 9, 2026
1.6.0 Stable Jan 9, 2026
1.5.2 Stable Jan 9, 2026
1.7.x-dev Dev Jan 9, 2026
1.6.x-dev Dev Jan 9, 2026
1.5.1 Stable Aug 8, 2025
1.5.0 Stable Jun 23, 2025
1.5.0-rc2 Pre-release Apr 1, 2025