Building functional tests for controllers and formsfor Drupal 8 , 9 , 10 , and 11

Last updated :  

Introduction

Welcome to our comprehensive exploration of functional testing in Drupal! Functional tests are crucial for evaluating whether different components of your module, such as controllers and forms, work together seamlessly within the context of a complete Drupal site. This lesson will guide you through creating tests that mimic user interactions to ensure that your modules perform as intended.

Understanding Functional Testing

Functional tests in Drupal verify that various parts of your application (especially forms and controllers) integrate and perform as expected. They simulate real-world user scenarios, including form submissions, button clicks, and user navigation, to ensure everything operates smoothly.

Benefits of Functional Testing

  • Comprehensive Validation: Tests interactions between components, catching integration issues.
  • Automated User Simulation: Simulates user behavior across your application, ensuring user-centric outcomes.
  • Confidence in Deployments: Reduces risk of failure when deploying new code or updates.

Setting Up Functional Tests for Your Module

For this example, we’ll use the mymodule module once again, demonstrating how to test a simple controller and a form implementation.

Step 1: Define a Simple Controller

We'll start by defining a basic controller in src/Controller/SimpleController.php:



namespace Drupal\mymodule\Controller;

use Drupal\Core\Controller\ControllerBase;

/**
 * Returns responses for my module routes.
 */
class SimpleController extends ControllerBase {

  /**
   * Returns a simple page.
   */
  public function simplePage() {
    return [
      '#markup' =--> $this->t('This is a simple page.'),
    ];
  }

}

Step 2: Create a Simple Form

Create a simple form to test user input handling in src/Form/SimpleForm.php:



namespace Drupal\mymodule\Form;

use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;

class SimpleForm extends FormBase {

  /**
   * {@inheritdoc}
   */
  public function getFormId() {
    return 'simple_form';
  }

  /**
   * {@inheritdoc}
   */
  public function buildForm(array $form, FormStateInterface $form_state) {
    $form['username'] = [
      '#type' =--> 'textfield',
      '#title' => $this->t('Username'),
      '#required' => TRUE,
    ];

    $form['actions']['#type'] = 'actions';
    $form['actions']['submit'] = [
      '#type' => 'submit',
      '#value' => $this->t('Submit'),
    ];

    return $form;
  }

  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state) {
    $this->messenger()->addMessage($this->t('Form has been submitted with username @username.', ['@username' => $form_state->getValue('username')]));
  }

}

Step 3: Write Functional Tests for the Defined Components

Save your test cases in the tests/src/Functional directory. Name your test file SimpleFunctionalTest.php.



namespace Drupal\Tests\mymodule\Functional;

use Drupal\Tests\BrowserTestBase;

/**
 * Tests for mymodule's functionality.
 *
 * @group mymodule
 */
class SimpleFunctionalTest extends BrowserTestBase {

  /**
   * {@inheritdoc}
   */
  public static $modules = ['mymodule'];

  /**
   * Test the simple page.
   */
  public function testSimplePage() {
    $this--->drupalGet('mymodule/simple');
    $this->assertSession()->statusCodeEquals(200);
    $this->assertSession()->pageTextContains('This is a simple page.');
  }

  /**
   * Test the simple form submission.
   */
  public function testSimpleForm() {
    $this->drupalGet('mymodule/simple-form');
    $this->assertSession()->pageTextContains('Username');

    $edit = [
      'username' => 'testuser',
    ];
    $this->submitForm($edit, 'Submit');
    $this->assertSession()->pageTextContains('Form has been submitted with username testuser.');
  }
}

Explanation:

These tests ensure that our simple page and form are behaving correctly. They visit the page or form, perform interactions, such as form submissions, and validate the expected outcomes, like status codes and messages.

Executing Functional Tests

To run your functional tests, use the following command:


# Run functional tests
vendor/bin/phpunit -c core/phpunit.xml.dist modules/custom/mymodule/tests/src/Functional

Conclusion

Functional testing in Drupal provides a powerful mechanism to ensure your controllers and forms function correctly within the full application context. This lesson equips you to create robust functional tests that verify user interaction integrity across your modules.

In our next lesson, we'll explore the use of mocks for dependency injection in tests, providing even more control over how your test environment behaves. Stick with us as we advance your Drupal module development capabilities!