Skip to content

[#645] Fixed 'emailBeforeScenario' bootstrap ordering under 'drupal/drupal-extension' 6.x.#649

Merged
AlexSkrypnyk merged 1 commit into
mainfrom
feature/645-email-bootstrap
Jun 1, 2026
Merged

[#645] Fixed 'emailBeforeScenario' bootstrap ordering under 'drupal/drupal-extension' 6.x.#649
AlexSkrypnyk merged 1 commit into
mainfrom
feature/645-email-bootstrap

Conversation

@AlexSkrypnyk
Copy link
Copy Markdown
Member

@AlexSkrypnyk AlexSkrypnyk commented May 31, 2026

Closes #645

Summary

EmailTrait::emailBeforeScenario() calls \Drupal::config() (via emailEnableTestSystem()emailGetMailSystemDefault()) before the Drupal kernel is guaranteed to be booted. Under drupal/drupal-extension 6.x the driver bootstraps lazily inside getDriver(), and Behat does not guarantee ordering between BeforeScenario hooks on different traits. On any scenario tagged @api @email, if the email hook fires before another trait triggers the bootstrap, the first \Drupal::config() call throws Drupal\Core\DependencyInjection\ContainerNotInitializedException and the scenario fails before its first step.

The fix is one line: call $this->getDriver() at the top of emailBeforeScenario() (after the existing tag guards) to prime the lazy bootstrap. This matches the pattern documented in OverrideTrait::overrideBootstrapDrupal() and removes the need for consumers to monkey-patch the hook in their own FeatureContext.

Changes

src/Drupal/EmailTrait.php

Added $this->getDriver(); in emailBeforeScenario() after the behat-steps-skip and @email tag guards, with a code comment explaining the lazy-bootstrap reason. The call is idempotent — when the kernel is already booted by another trait's hook, it is a no-op.

tests/behat/bootstrap/BehatCliTrait.php

The generated FeatureContext.php template used by @trait: subprocess scenarios includes a bootstrapDrupal() workaround method that primes the lazy driver. That workaround was silently masking the bug in EmailTrait (and the same bug latent in other traits whose hooks call \Drupal::). Extracted the workaround into a {{BOOTSTRAP_METHOD}} template token, gated on a new bool $bootstrap_workaround = TRUE parameter on behatCliWriteFeatureContextFile(). Default is TRUE so every existing @trait: scenario behaves identically.

Added a tag scan in behatCliBeforeScenario(): scenarios tagged @behat-cli-no-bootstrap cause the generated context to omit the workaround, allowing a trait to be exercised in true isolation.

tests/behat/features/drupal_email.feature

Added one regression scenario tagged @trait:Drupal\EmailTrait @behat-cli-no-bootstrap that runs an @api @email inner subprocess and asserts it passes. Before the fix the scenario fails with \Drupal::$container is not initialized yet. \Drupal::setContainer() must be called with a real container. (Drupal\Core\DependencyInjection\ContainerNotInitializedException) reported on the FeatureContext::emailBeforeScenario() hook. After the fix the scenario passes.

Before / After

BEFORE:                                       AFTER:

@api @email scenario                          @api @email scenario
       │                                             │
       ▼                                             ▼
EmailTrait::emailBeforeScenario               EmailTrait::emailBeforeScenario
       │                                             │
       │ (no bootstrap call)                         │ $this->getDriver()
       ▼                                             │  ───► kernel booted
\Drupal::config('system.mail')                       ▼
       │                                      \Drupal::config('system.mail')
       ▼                                             │
ContainerNotInitializedException ✗                   ▼
                                              test_mail_collector enabled ✓

Summary

This PR fixes a race condition in EmailTrait::emailBeforeScenario() under drupal/drupal-extension 6.x, where the method could call \Drupal::config() before the Drupal kernel was guaranteed to be booted. The fix ensures the lazy-loading 6.x driver is primed by calling $this->getDriver() at the start of the hook (after existing tag guards), making the trait self-sufficient when used in isolation.

Changes

src/Drupal/EmailTrait.php

  • Added $this->getDriver() call in emailBeforeScenario() (line 66) to force Drupal bootstrap before \Drupal::config() is accessed
  • The call is placed after existing scenario tag guards and is idempotent
  • Includes explanatory comment about lazy 6.x driver bootstrap behavior

tests/behat/bootstrap/BehatCliTrait.php

  • Updated behatCliBeforeScenario() to detect the @behat-cli-no-bootstrap tag and pass a $bootstrap_workaround boolean flag
  • Extended behatCliWriteFeatureContextFile() signature: added bool $bootstrap_workaround = TRUE parameter
  • When $bootstrap_workaround is enabled (default), the generated FeatureContext template includes a bootstrapDrupal() method annotated with #[BeforeScenario @api] that calls $this->getDriver()
  • When disabled (via @behat-cli-no-bootstrap tag), the workaround is omitted to test traits in isolation
  • Implemented via {{BOOTSTRAP_METHOD}} token replacement in the template

tests/behat/features/drupal_email.feature

  • Added regression scenario (lines 692-704) tagged with @trait:Drupal\EmailTrait and @behat-cli-no-bootstrap
  • Scenario verifies that EmailTrait bootstraps the Drupal kernel itself when used in isolation by running an @api @email`` inner subprocess
  • Uses only existing step definitions; no new steps added
  • Asserts the subprocess scenario passes

Acceptance Criteria Met

✓ EmailTrait::emailBeforeScenario() calls $this->getDriver() after tag guards
@api @email scenarios run standalone without depending on other traits for bootstrap
✓ getDriver() call is idempotent
✓ Regression test exercises the trait in isolation
✓ Generated FeatureContext template supports bootstrap workaround omission via @behat-cli-no-bootstrap tag

Code Quality

  • No violations of CONTRIBUTING.md step definition rules (no new step definitions introduced)
  • No alterations to exported or public entity signatures (only method parameter additions with defaults)
  • Total changes: +50 lines, -12 lines across three files

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 31, 2026

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: b3b5c96d-2c13-4615-8402-87b013109c10

📥 Commits

Reviewing files that changed from the base of the PR and between 9ccc5d9 and a6dc28a.

📒 Files selected for processing (3)
  • src/Drupal/EmailTrait.php
  • tests/behat/bootstrap/BehatCliTrait.php
  • tests/behat/features/drupal_email.feature

Walkthrough

EmailTrait now self-boots the Drupal kernel by calling $this->getDriver() before accessing config, eliminating hook-ordering failures under lazy 6.x drivers. The test harness gains conditional bootstrap support via a new @behat-cli-no-bootstrap tag, and a new scenario validates trait isolation.

Changes

Drupal lazy bootstrap ordering fix for EmailTrait

Layer / File(s) Summary
EmailTrait kernel bootstrap fix
src/Drupal/EmailTrait.php
EmailTrait::emailBeforeScenario() calls $this->getDriver() before Drupal::config() to force lazy kernel bootstrap, eliminating container-not-initialized failures when BeforeScenario hooks fire out of order.
Conditional bootstrap method in test harness
tests/behat/bootstrap/BehatCliTrait.php
behatCliBeforeScenario() detects @behat-cli-no-bootstrap tag and passes a bootstrap_workaround flag to behatCliWriteFeatureContextFile(). The generated FeatureContext template conditionally includes a bootstrapDrupal() hook via {{BOOTSTRAP_METHOD}} token, allowing scenarios to run with or without automatic bootstrap.
Isolation test for EmailTrait bootstrap
tests/behat/features/drupal_email.feature
New scenario tagged @trait:Drupal\EmailTrait and @behat-cli-no-bootstrap runs Behat in isolation, verifying that EmailTrait successfully boots the kernel without a standard FeatureContext bootstrap hook.

🎯 2 (Simple) | ⏱️ ~12 minutes


🐰 A lazy kernel wakes at last,
No more ordering woes to cast,
EmailTrait stands alone,
Bootstrap its own,
Isolation tests make us dance! 🎉

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main fix: calling getDriver() to address emailBeforeScenario bootstrap ordering in drupal-extension 6.x. It is concise and specific to the primary change.
Linked Issues check ✅ Passed All acceptance criteria from issue #645 are met: emailBeforeScenario() calls getDriver() [src/Drupal/EmailTrait.php], @api @email scenarios run standalone [tests/behat/features/drupal_email.feature], and the fix is idempotent as required.
Out of Scope Changes check ✅ Passed All changes are directly scoped to fixing the bootstrap ordering issue and adding test coverage. The BehatCliTrait changes support the fix by enabling the regression test via template injection.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/645-email-bootstrap

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov
Copy link
Copy Markdown

codecov Bot commented Jun 1, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 96.54%. Comparing base (9ccc5d9) to head (a6dc28a).

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #649   +/-   ##
=======================================
  Coverage   96.54%   96.54%           
=======================================
  Files          44       44           
  Lines        3355     3356    +1     
=======================================
+ Hits         3239     3240    +1     
  Misses        116      116           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@AlexSkrypnyk AlexSkrypnyk merged commit 5f2030d into main Jun 1, 2026
15 checks passed
@AlexSkrypnyk AlexSkrypnyk deleted the feature/645-email-bootstrap branch June 1, 2026 00:43
@AlexSkrypnyk AlexSkrypnyk modified the milestones: v3.9, v3.10 Jun 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Fix 'emailBeforeScenario' bootstrap ordering under 'drupal/drupal-extension' 6.x.

1 participant