Principle:Webdriverio Webdriverio Test Framework Integration
| Knowledge Sources | |
|---|---|
| Domains | Test_Framework_Integration, Hook_Management |
| Last Updated | 2026-02-12 00:00 GMT |
Overview
Adapting third-party test runner interfaces to inject lifecycle hooks, enable automatic retries, and bridge asynchronous browser commands with synchronous test assertions.
Description
Test frameworks such as Mocha and Jasmine define their own interfaces for declaring tests (describe, it, beforeEach). A browser automation test runner must wrap these interfaces to inject its own lifecycle hooks (session setup/teardown, screenshot capture, result reporting) without requiring test authors to manually call framework-specific setup code. Test Framework Integration intercepts the registration of test functions and wraps them in an adapter that: executes before/after hooks at the appropriate granularity (suite, test, step), provides retry logic for flaky tests (re-executing the test function a configurable number of times on failure), and ensures that asynchronous browser commands integrate correctly with the test framework's async handling. This creates a seamless authoring experience where test code reads naturally while the framework handles all orchestration behind the scenes.
Usage
This principle applies when a browser automation framework is built on top of an existing test runner rather than implementing its own test execution engine. It is the right choice when teams want to use familiar testing patterns (BDD-style describe/it blocks, setup/teardown hooks) while gaining automatic browser session management, failure screenshots, and retry capabilities. It is essential for maintaining compatibility with the broader testing ecosystem (reporters, plugins, IDE integrations) that expect standard test runner interfaces.
Theoretical Basis
The integration uses a function wrapping and interposition strategy:
- Interface wrapping: The original test declaration functions (
describe,it,before,after) are replaced with wrapped versions that intercept the user-provided callback:
function wrapTestInterface(originalIt):
return function wrappedIt(title, testFn, timeout):
originalIt(title, wrapTestFunction(testFn), timeout)
function wrapTestFunction(testFn):
return async function():
context = getCurrentTestContext()
await executeHooks(context.beforeHooks)
try:
result = await executeWithRetry(testFn, context.retryCount)
await executeHooks(context.afterHooks, {passed: true})
return result
catch error:
await executeHooks(context.afterHooks, {passed: false, error})
throw error
- Retry logic: When a test function throws an error and retries are configured, the wrapper re-executes the function up to N times. Each retry resets the test state (browser navigation, session cookies) to prevent cascading failures:
function executeWithRetry(testFn, maxRetries):
lastError = null
for attempt in 0..maxRetries:
try:
return await testFn()
catch error:
lastError = error
if attempt < maxRetries:
await resetTestState()
throw lastError
- Hook ordering: Hooks execute in a defined order that respects both the test framework's native hook system and the automation framework's hooks. The general ordering is: framework beforeAll, automation beforeSuite, framework beforeEach, automation beforeTest, test function, automation afterTest, framework afterEach, and so on. This layered ordering prevents conflicts between the two hook systems.
- Async bridging: The wrapper ensures that promises returned by browser commands are properly awaited before the test framework's timeout mechanism fires, preventing false timeout failures on slow browser operations.