Jump to content

Connect SuperML | Leeroopedia MCP: Equip your AI agents with best practices, code verification, and debugging knowledge. Powered by Leeroo — building Organizational Superintelligence. Contact us at founders@leeroo.com.

Implementation:DevExpress Testcafe TestedApp Start Kill

From Leeroopedia
Knowledge Sources
Domains Testing, CI_CD, Web_Automation
Last Updated 2026-02-12 04:00 GMT

Overview

Process management class for starting and terminating the application under test, with stdout/stderr logging and error detection.

Description

The TestedApp class provides lifecycle management for the application being tested. It uses the execa library to spawn application processes with shell command parsing, augments the PATH environment variable to include node_modules/.bin for local tool access, captures and logs stdout/stderr streams via debug loggers, monitors for premature process termination, implements configurable initialization delays, and uses tree-kill to terminate the entire process tree with SIGTERM.

Key implementation details:

  • Process Spawning: Uses `execa.command()` in shell mode for command string parsing
  • Environment Setup: Prepends node_modules/.bin to PATH for local binary resolution
  • Stream Logging: Captures stdout/stderr with debug namespaces (testcafe:tested-app:stdout/stderr)
  • Error Promise: Creates promise that rejects if application crashes unexpectedly
  • Initialization Race: Waits for either initDelay timeout or application error
  • Graceful Shutdown: Uses tree-kill with SIGTERM to stop entire process hierarchy
  • Crash Suppression: Sets _killed flag to ignore expected termination errors

Usage

Use this implementation when:

  • Running tests against local development servers (Express, webpack-dev-server, etc.)
  • Configuring TestCafe to automatically start tested applications
  • Debugging application startup issues in test contexts
  • Ensuring clean application state between test runs
  • Managing application lifecycle in CI pipelines

Code Reference

Source Location

  • Repository: testcafe
  • File: src/runner/tested-app.ts
  • Lines: 39-98

Signature

class TestedApp {
    private _process: null | ChildProcess;
    private _killed: boolean;
    private _stdoutLogger: debug.Debugger;
    private _stderrLogger: debug.Debugger;
    public errorPromise: null | Promise<void>;

    constructor()
    async start(command: string, initDelay: number): Promise<void>
    async kill(): Promise<void>
    private async _run(command: string): Promise<void>
}

Import

// Internal TestCafe usage
import TestedApp from './runner/tested-app';

// Usage in test runner
const app = new TestedApp();
await app.start('node server.js', 3000);
// ... run tests ...
await app.kill();

I/O Contract

Inputs

Name Type Required Description
command string Yes Shell command to start application (e.g., "node server.js")
initDelay number Yes Milliseconds to wait for app initialization (e.g., 3000)

Outputs

Name Type Description
stdout logged Application stdout captured to debug logger
stderr logged Application stderr captured to debug logger
errorPromise Promise<void> Promise that rejects if app crashes
exit_code number Process exit code (available after kill())

Usage Examples

CLI Usage

# Start Node.js server before tests
testcafe chrome tests/ \
  --app "node server.js" \
  --app-init-delay 3000

# Start with npm script
testcafe chrome tests/ \
  --app "npm start" \
  --app-init-delay 5000

# Start webpack-dev-server
testcafe chrome tests/ \
  --app "webpack serve --port 8080" \
  --app-init-delay 10000

# Start with environment variables
testcafe chrome tests/ \
  --app "PORT=3000 NODE_ENV=test node server.js" \
  --app-init-delay 2000

Configuration File

// .testcaferc.js
module.exports = {
    src: 'tests/**/*.js',
    browsers: 'chrome:headless',

    // Application lifecycle
    appCommand: 'node server.js',
    appInitDelay: 3000,  // 3 seconds

    // Alternative: npm script
    // appCommand: 'npm run dev',
    // appInitDelay: 5000
};

Programmatic API

const createTestCafe = require('testcafe');

(async () => {
    const testcafe = await createTestCafe('localhost', 1337, 1338);
    const runner = testcafe.createRunner();

    const failedCount = await runner
        .src('tests/**/*.js')
        .browsers('chrome:headless')
        .startApp('node server.js', 3000)  // Start app with 3s delay
        .run();

    console.log('Tests failed: ' + failedCount);
    await testcafe.close();  // Automatically kills app
})();

Internal Implementation Flow

// How TestCafe uses TestedApp internally

// 1. Create TestedApp instance
const testedApp = new TestedApp();

// 2. Start application before tests
await testedApp.start('node server.js', 3000);

try {
    // 3. Run tests while application is running
    await runTests();

    // 4. Check if app crashed during tests
    if (testedApp.errorPromise) {
        // errorPromise rejects if app crashed
        await Promise.race([
            runTests(),
            testedApp.errorPromise
        ]);
    }
} finally {
    // 5. Always kill application after tests
    await testedApp.kill();
}

Debugging Application Startup

# Enable debug logging to see app stdout/stderr
DEBUG=testcafe:tested-app:* testcafe chrome tests/ \
  --app "node server.js" \
  --app-init-delay 3000

# Output:
# testcafe:tested-app:stdout Server listening on port 3000
# testcafe:tested-app:stdout Database connected
# testcafe:tested-app:stdout Routes initialized

Error Scenarios

// Scenario 1: Application fails to start (syntax error)
// appCommand: "node server-with-error.js"
// Error: RUNTIME_ERRORS.testedAppFailedWithError
// Message: "SyntaxError: Unexpected token..."
// Tests never run

// Scenario 2: Application crashes during tests
try {
    await app.start('node unstable-server.js', 1000);
    await runTests();
} catch (error) {
    // If app.errorPromise rejects during tests:
    // Error: "Application crashed unexpectedly"
    // Current test fails
}

// Scenario 3: Application port conflict
// appCommand: "node server.js"  (port 3000 already in use)
// Error: "listen EADDRINUSE: address already in use :::3000"
// Tests never run

Process Tree Example

# Application spawns child processes
# appCommand: "npm start"

# Process tree:
# sh -c "npm start" (PID 1000)
#   └─ npm (PID 1001)
#       └─ node server.js (PID 1002)
#           ├─ worker thread (PID 1003)
#           └─ cluster worker (PID 1004)

# app.kill() uses tree-kill to terminate all:
# Kills: 1004, 1003, 1002, 1001, 1000 (depth-first)
# Signal: SIGTERM (graceful shutdown)

PATH Augmentation Behavior

// Before PATH augmentation:
// PATH=/usr/bin:/usr/local/bin

// Command: "webpack serve"
// Resolves to: /usr/local/bin/webpack (global install)

// After PATH augmentation by TestedApp:
// PATH=/project/node_modules/.bin:/usr/bin:/usr/local/bin

// Command: "webpack serve"
// Resolves to: /project/node_modules/.bin/webpack (local install)

// This ensures local project tools are used, not global versions

Initialization Delay Guidelines

// Choose initDelay based on application type:

// Static file server (serve, http-server)
appInitDelay: 100  // Very fast, just bind to port

// Express API (no database)
appInitDelay: 1000  // Fast, load routes

// Express API (with database)
appInitDelay: 3000  // Medium, connect to DB

// Full-stack app (Webpack Dev Server, Next.js)
appInitDelay: 10000  // Slow, compile assets

// Legacy app (complex initialization)
appInitDelay: 30000  // Very slow, multiple services

// Too short: Tests may fail because app not ready
// Too long: Wastes time waiting unnecessarily
// Best practice: Use smallest delay that reliably works

CI Pipeline Example

# GitHub Actions
- name: Run TestCafe tests
  run: |
    testcafe chrome:headless tests/ \
      --app "node server.js" \
      --app-init-delay 3000 \
      --reporter xunit:junit.xml

# Application automatically:
# 1. Starts before tests
# 2. Runs during tests
# 3. Terminates after tests (even if tests fail)
# 4. Cleans up all child processes

Related Pages

Implements Principle

Page Connections

Double-click a node to navigate. Hold to expand connections.
Principle
Implementation
Heuristic
Environment