Skip to main content

Configuration

This page provides a complete reference for all wdio-api-runner configuration options. From basic setup to advanced customization, you'll find everything you need to configure your API testing environment.

Overview​

wdio-api-runner configuration lives in your wdio.conf.ts file alongside standard WebdriverIO options. The runner introduces two main configuration sections:

SectionPurpose
apiRunnerHTTP client configuration (timeout, headers, retries)
graphqlRunnerGraphQL client configuration (endpoint, subscriptions)

All standard WebdriverIO options (specs, reporters, services, etc.) work exactly as documented in the WebdriverIO Configuration Reference.

Complete Configuration Example​

Here's a full configuration file demonstrating all available options:

// wdio.conf.ts
import type { Options } from '@wdio/types'

export const config: Options.Testrunner = {
//
// ====================
// Runner Configuration
// ====================
// Use the API runner (required)
runner: 'api',

//
// ==================
// Specify Test Files
// ==================
// Define locations of your test spec files
specs: [
'./test/api/**/*.spec.ts'
],

// Exclude specific files from test runs
exclude: [
'./test/api/**/*.skip.ts',
'./test/api/helpers/**/*.ts'
],

//
// ============
// Capabilities
// ============
// For proper reporter output, configure API capabilities
capabilities: [{
browserName: 'api',
platformName: 'API Runner',
browserVersion: 'stable',
}],

//
// ===================
// Test Configurations
// ===================
// Maximum parallel workers
maxInstances: 10,

// Test framework
framework: 'mocha',
mochaOpts: {
ui: 'bdd',
timeout: 60000,
retries: 1
},

// Reporters
reporters: [
'spec',
['allure', {
outputDir: 'allure-results',
disableWebdriverStepsReporting: true
}],
['junit', {
outputDir: './reports/junit',
outputFileFormat: (options) => `results-${options.cid}.xml`
}]
],

//
// =============
// API Base URL
// =============
// Base URL for all relative API paths
baseUrl: 'https://api.example.com',

//
// =================
// API Runner Config
// =================
apiRunner: {
// Request timeout in milliseconds
timeout: 30000,

// Retry configuration
retries: 0,
retryDelay: 1000,

// Default headers for all requests
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
'X-API-Version': '2024-01'
},

// Enable verbose request/response logging
verbose: false,

// Custom global variable name (default: 'api')
globalName: 'api',
},

//
// =====================
// GraphQL Runner Config
// =====================
graphqlRunner: {
endpoint: 'https://api.example.com/graphql',
headers: {
'Content-Type': 'application/json'
},
timeout: 30000,
webSocket: {
url: 'wss://api.example.com/graphql',
connectionParams: {
authToken: process.env.WS_TOKEN
}
}
},

//
// =======
// Logging
// =======
// Group logs by spec file for cleaner CI output
groupLogsByTestSpec: true,

// Output directory for logs
outputDir: './logs',

// Log level
logLevel: 'info',

//
// =====
// Hooks
// =====
onPrepare: async function (config, capabilities) {
console.log('Starting test run...')
},

beforeSession: async function (config, capabilities, specs) {
// Setup before each worker session
},

before: async function (capabilities, specs) {
// Setup before tests in each worker
},

afterTest: async function (test, context, { error, result, duration, passed }) {
// After each test
},

after: async function (result, capabilities, specs) {
// Cleanup after tests in each worker
},

onComplete: async function (exitCode, config, capabilities, results) {
console.log('Test run complete!')
}
}

API Runner Options​

apiRunner.timeout​

Default timeout for all HTTP requests in milliseconds. If a request takes longer than this, it will be aborted and the test will fail.

apiRunner: {
timeout: 30000 // 30 seconds (default)
}

Per-request override:

const response = await api.get('/slow-endpoint', {
timeout: 120000 // 2 minutes for this specific request
})

apiRunner.retries​

Number of automatic retry attempts for failed requests. Useful for handling transient network issues or rate limiting.

apiRunner: {
retries: 3 // Retry up to 3 times on failure
}

When retries occur:

  • Network errors (connection refused, timeout)
  • HTTP 5xx server errors
  • HTTP 429 Too Many Requests

When retries do NOT occur:

  • HTTP 4xx client errors (except 429)
  • Successful responses (any 2xx status)

apiRunner.retryDelay​

Delay between retry attempts in milliseconds. This helps avoid overwhelming a struggling server.

apiRunner: {
retries: 3,
retryDelay: 1000 // Wait 1 second between retries
}

For exponential backoff, consider using request interceptors (see Interceptors).

apiRunner.headers​

Default headers sent with every request. These can be overridden on a per-request basis.

apiRunner: {
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
'X-API-Key': process.env.API_KEY,
'X-Client-Version': '1.0.0'
}
}

Dynamic headers: For headers that need to be computed at runtime (like authentication tokens), use request interceptors:

// In your test setup or beforeSession hook
api.addRequestInterceptor(async (config) => {
config.headers.set('Authorization', `Bearer ${await getToken()}`)
return config
})

apiRunner.verbose​

Enable detailed logging of all requests and responses. Helpful for debugging but can be noisy in CI.

apiRunner: {
verbose: true
}

Output example:

→ GET https://api.example.com/users/1
Headers: { "Accept": "application/json" }

← 200 OK (145ms)
Headers: { "Content-Type": "application/json" }
Body: { "id": 1, "name": "John Doe" }

apiRunner.client​

Use a custom API client instead of the built-in one. Your client must implement the ApiClient interface.

import CustomApiClient from './helpers/CustomApiClient'

apiRunner: {
client: CustomApiClient
}

Custom client interface:

interface ApiClient {
get<T>(path: string, options?: RequestOptions): Promise<ApiResponse<T>>
post<T>(path: string, body?: unknown, options?: RequestOptions): Promise<ApiResponse<T>>
put<T>(path: string, body?: unknown, options?: RequestOptions): Promise<ApiResponse<T>>
patch<T>(path: string, body?: unknown, options?: RequestOptions): Promise<ApiResponse<T>>
delete<T>(path: string, options?: RequestOptions): Promise<ApiResponse<T>>
// ... additional methods
}

apiRunner.clientFactory​

Factory function for creating API clients dynamically. Called once per worker with the current configuration.

apiRunner: {
clientFactory: (config) => {
const client = new ApiClient()
client.setBaseUrl(config.baseUrl)

// Add environment-specific setup
if (process.env.DEBUG) {
client.enableVerboseLogging()
}

return client
}
}

apiRunner.globalName​

Change the global variable name used to access the API client. Default is api.

apiRunner: {
globalName: 'http' // Access via `http.get()` instead of `api.get()`
}

In your tests:

// With globalName: 'http'
const response = await http.get('/users/1')

GraphQL Runner Options​

graphqlRunner.endpoint​

The GraphQL endpoint URL for queries and mutations.

graphqlRunner: {
endpoint: 'https://api.example.com/graphql'
}

graphqlRunner.headers​

Default headers for GraphQL requests.

graphqlRunner: {
endpoint: 'https://api.example.com/graphql',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${process.env.GRAPHQL_TOKEN}`
}
}

graphqlRunner.timeout​

Timeout for GraphQL requests in milliseconds.

graphqlRunner: {
timeout: 30000
}

graphqlRunner.webSocket​

WebSocket configuration for GraphQL subscriptions.

graphqlRunner: {
endpoint: 'https://api.example.com/graphql',
webSocket: {
// WebSocket URL for subscriptions
url: 'wss://api.example.com/graphql',

// Connection parameters (sent on connection)
connectionParams: {
authToken: process.env.WS_AUTH_TOKEN
},

// Reconnection settings
reconnect: true,
reconnectAttempts: 5,
reconnectInterval: 3000
}
}

graphqlRunner.sse​

Server-Sent Events configuration for GraphQL subscriptions.

graphqlRunner: {
endpoint: 'https://api.example.com/graphql',
sse: {
url: 'https://api.example.com/graphql/stream',
headers: {
'Authorization': `Bearer ${process.env.SSE_TOKEN}`
}
}
}

Environment Variables​

Override configuration at runtime using environment variables. This is useful for CI/CD pipelines and multi-environment testing.

WDIO_API_OPTIONS​

JSON-formatted object to override apiRunner options.

# Override base URL and timeout
WDIO_API_OPTIONS='{"baseUrl":"https://staging.api.com","timeout":60000}'

# Run tests with overrides
WDIO_API_OPTIONS='{"baseUrl":"https://staging.api.com"}' npx wdio run wdio.conf.ts

Common Environment Variables​

# API base URL
API_BASE_URL=https://staging.api.example.com

# Authentication
API_KEY=your-api-key
API_TOKEN=your-bearer-token

# SSL handling (development only)
NODE_TLS_REJECT_UNAUTHORIZED=0

# Debug logging
DEBUG=wdio-api-runner:*

Using in configuration:

baseUrl: process.env.API_BASE_URL || 'https://api.example.com',

apiRunner: {
headers: {
'X-API-Key': process.env.API_KEY || ''
}
}

Capabilities Configuration​

For proper reporter output and test identification, configure capabilities:

capabilities: [{
browserName: 'api',
platformName: 'API Runner',
browserVersion: 'stable',
}]

For multi-environment testing:

capabilities: [
{
browserName: 'api',
platformName: 'Production',
'custom:baseUrl': 'https://api.example.com'
},
{
browserName: 'api',
platformName: 'Staging',
'custom:baseUrl': 'https://staging.api.example.com'
}
]

Framework Configuration​

Mocha (Default)​

framework: 'mocha',
mochaOpts: {
// BDD-style (describe/it) or TDD-style (suite/test)
ui: 'bdd',

// Test timeout in milliseconds
timeout: 60000,

// Retry failed tests
retries: 1,

// Bail on first failure
bail: false,

// Grep pattern to filter tests
grep: process.env.TEST_GREP || undefined,

// Invert grep (run tests that DON'T match)
invert: false
}

Jasmine​

framework: 'jasmine',
jasmineOpts: {
// Default timeout for async tests
defaultTimeoutInterval: 60000,

// Random execution order
random: false,

// Seed for random order
seed: '12345',

// Stop on first failure
stopOnFailure: false,

// Fail fast
stopSpecOnExpectationFailure: false
}

Cucumber​

framework: 'cucumber',
cucumberOpts: {
// Step definition files
require: ['./test/steps/**/*.ts'],

// Support files
requireModule: ['ts-node/register'],

// Tag expression to filter scenarios
tags: '@api and not @skip',

// Fail on undefined steps
failAmbiguousDefinitions: true,

// Retry failed scenarios
retry: 2,

// Format for output
format: ['pretty'],

// Parallel scenarios
parallel: 2
}

Reporter Configuration​

Spec Reporter​

Simple console output showing test results.

reporters: ['spec']

Allure Reporter​

Rich HTML reports with attachments.

reporters: [
['allure', {
outputDir: 'allure-results',
disableWebdriverStepsReporting: true,
disableWebdriverScreenshotsReporting: true
}]
]

JUnit Reporter​

XML reports for CI/CD integration.

reporters: [
['junit', {
outputDir: './reports/junit',
outputFileFormat: (options) => `results-${options.cid}.xml`,
suiteNameFormat: /[^/]+$/
}]
]

Multiple Reporters​

Combine multiple reporters for different outputs.

reporters: [
'spec',
['allure', { outputDir: 'allure-results' }],
['junit', { outputDir: './reports/junit' }]
]

Logging Configuration​

Log Levels​

// Available levels: trace, debug, info, warn, error, silent
logLevel: 'info'

Log Grouping​

Group logs by spec file for cleaner CI output (especially useful in GitHub Actions).

groupLogsByTestSpec: true

Output Directory​

Directory for log files.

outputDir: './logs'

Hooks​

WebdriverIO hooks let you run code at specific points in the test lifecycle.

Common Hook Use Cases​

// Before the entire test run
onPrepare: async function (config, capabilities) {
// Seed database, start mock server, etc.
await seedDatabase()
},

// Before each worker session
beforeSession: async function (config, capabilities, specs) {
// Worker-specific setup
},

// Before tests in each worker
before: async function (capabilities, specs) {
// Add custom commands, interceptors, etc.
api.addRequestInterceptor(authInterceptor)
},

// After each test
afterTest: async function (test, context, { error, result, duration, passed }) {
if (!passed) {
// Log request/response details on failure
console.log('Failed test:', test.title)
}
},

// After tests in each worker
after: async function (result, capabilities, specs) {
// Worker-specific cleanup
},

// After the entire test run
onComplete: async function (exitCode, config, capabilities, results) {
// Generate reports, cleanup resources, etc.
await generateReport()
}

Next Steps​

Now that you understand the configuration options:

  • API Client — Learn the HTTP client API in detail
  • Assertions — Use fluent assertions for cleaner tests
  • Interceptors — Add middleware for auth, logging, retries
  • CI Integration — Configure for GitHub Actions, GitLab CI, etc.