Skip to content

Functional Testing Concepts

When to Use

Use functional tests when the feature involves configuration, routing, permissions, or multi-request workflows that integration tests cannot easily exercise. Do not use them for logic variants — those belong in unit tests.

Decision

Term Scope Browser? Real DB?
Integration Two or more components in the same process No Typically yes (test DB)
Functional / System Feature-level behavior, full stack in-process No Yes (test DB)
E2E User journey through real browser + real stack Yes Yes

Functional tests are the right choice when: - The feature requires Drupal's permission system (access callbacks, route requirements) - The workflow spans multiple HTTP requests (wizard-style forms, OAuth redirects) - You need to test routing or middleware that integration tests cannot reach - You want to test an entire user story without the overhead of a real browser

Pattern

// Drupal functional test (PHPUnit BrowserTestBase)
// Tests the feature from the outside: HTTP → Drupal → DB
class UserRegistrationTest extends BrowserTestBase {
  public function testRegistrationCreatesUserAndSendsEmail(): void {
    $this->drupalGet('user/register');
    $this->submitForm([
      'mail' => 'alice@example.com',
      'pass[pass1]' => 'S3cretPass!',
      'pass[pass2]' => 'S3cretPass!',
    ], 'Create new account');
    $this->assertSession()->statusCodeEquals(200);
    $this->assertSession()->pageTextContains('Registration successful');
    $user = user_load_by_mail('alice@example.com');
    $this->assertNotNull($user);
  }
}

Common Mistakes

  • Wrong: Using functional tests for logic that unit tests can cover → Right: Functional tests are slow; logic belongs at the unit level
  • Wrong: Testing every logic variant through a functional test → Right: Test variants with unit tests; use functional for the integration wire-up
  • Wrong: Not cleaning up state between functional tests → Right: Use proper setup/teardown; leftover state causes intermittent failures
  • Wrong: Conflating "functional" with "E2E" → Right: Functional runs in-process; E2E uses a real browser — different costs and failure modes

See Also