Principle:DevExpress Testcafe Tested Application Management
| Knowledge Sources | |
|---|---|
| Domains | Testing, CI_CD, Web_Automation |
| Last Updated | 2026-02-12 04:00 GMT |
Overview
Tested Application Management is the automatic starting and stopping of the application under test as part of the test lifecycle, ensuring the tested server is available before tests begin and cleaned up after tests complete.
Description
End-to-end tests require a running application to test against. Manually starting the application server before tests and remembering to stop it afterward is error-prone and incompatible with CI automation. Tested Application Management solves this by treating the application lifecycle as an integral part of the test run: the test framework automatically spawns the application process, waits for initialization, runs tests, and terminates the application process tree.
Key aspects include:
- Process Spawning: Starting application via shell command with environment inheritance
- Initialization Delay: Configurable wait time for application to become ready
- PATH Augmentation: Adding node_modules/.bin to PATH for local tool resolution
- Output Capture: Logging application stdout/stderr for debugging
- Error Detection: Detecting premature application crashes before tests begin
- Graceful Termination: Killing entire process tree (parent and children) with SIGTERM
- Crash Suppression: Ignoring application termination errors during explicit shutdown
- CI Integration: Enabling headless test runs without manual server management
Usage
Use Tested Application Management when:
- Running end-to-end tests against local development servers
- Automating test execution in CI/CD pipelines
- Testing Node.js applications that need to be started before tests
- Running tests against applications with initialization requirements (database seeding, cache warming)
- Ensuring clean test environments by starting fresh application instances
- Debugging application startup issues in test contexts
- Managing application lifecycle for parallel test runs with different configurations
Theoretical Basis
Core Concept: The application under test is a subprocess with a lifecycle managed by the test framework. The framework acts as a process supervisor, monitoring application health and terminating it regardless of test outcome.
Application Lifecycle States:
NOT_STARTED -> STARTING -> RUNNING -> TERMINATING -> TERMINATED
| | |
| | +-> ERROR (crashed)
| +-> READY (after initDelay)
+-> ERROR (failed to start)
Process Management Algorithm:
CLASS TestedApp:
process = null
killed = false
errorPromise = null
FUNCTION start(command, initDelay):
// 1. Spawn application process
this.process = SPAWN_COMMAND(command, {
shell: true,
env: AUGMENT_PATH(process.env),
stdio: 'pipe'
})
// 2. Setup output logging
this.process.stdout.on('data', data => LOG_STDOUT(data))
this.process.stderr.on('data', data => LOG_STDERR(data))
// 3. Setup error detection
this.errorPromise = WAIT_FOR_PROCESS_EXIT(this.process)
.then(() => {
IF NOT this.killed:
THROW ERROR("Application crashed unexpectedly")
})
// 4. Wait for initialization or error
AWAIT RACE([
DELAY(initDelay), // Normal case: wait for app to initialize
this.errorPromise // Error case: app crashed during startup
])
// 5. Application is ready for tests
RETURN
FUNCTION kill():
this.killed = true
// Kill entire process tree (parent + children)
AWAIT KILL_PROCESS_TREE(this.process.pid, 'SIGTERM')
// Process terminated successfully
PATH Augmentation:
// Ensure local node_modules/.bin tools are available
FUNCTION augmentPath(env):
currentPath = env.PATH || env.Path // Windows uses 'Path'
nodeModulesBin = RESOLVE_PATH('./node_modules/.bin')
// Prepend node_modules/.bin to PATH
env.PATH = nodeModulesBin + PATH_DELIMITER + currentPath
RETURN env
// Example:
// Original PATH: /usr/bin:/usr/local/bin
// Augmented PATH: /project/node_modules/.bin:/usr/bin:/usr/local/bin
// Now "webpack" command resolves to local webpack, not global
Initialization Delay Strategy:
// Why initialization delay is needed:
// 1. Server binds to port (fast, ~10ms)
// 2. Database connections established (medium, ~100ms)
// 3. Cache warming, asset compilation (slow, 1-10s)
// 4. Application routes become available
FUNCTION determineInitDelay(app):
IF app.type == 'static_server':
RETURN 100 // Very fast startup
IF app.type == 'express_api':
RETURN 1000 // Medium startup
IF app.type == 'full_stack':
IF app.hasDatabase:
RETURN 3000 // Slow startup with DB
ELSE:
RETURN 1500 // Medium-slow startup
// Default conservative delay
RETURN 5000
Process Tree Termination:
// Why tree-kill is necessary:
// - Simple process.kill() only kills parent process
// - Child processes (workers, database connections) remain running
// - Can cause port conflicts in subsequent test runs
FUNCTION killProcessTree(pid, signal):
// Find all child processes recursively
children = GET_CHILD_PIDS(pid)
// Kill children first (depth-first)
FOR EACH childPid IN REVERSE(children):
KILL(childPid, signal)
// Kill parent last
KILL(pid, signal)
// Wait for all processes to terminate
WAIT_FOR_TERMINATION(pid, timeout=5000)
// Example process tree:
// node server.js (PID 1234)
// ├─ webpack-dev-server (PID 1235)
// │ └─ webpack worker (PID 1236)
// └─ express server (PID 1237)
//
// killProcessTree(1234) kills 1236, 1235, 1237, then 1234
Error Handling:
// Three error scenarios:
// 1. Application fails to start (syntax error, missing dependencies)
FUNCTION handleStartupError(error):
LOG_ERROR("Application failed to start:", error)
THROW RUNTIME_ERROR.testedAppFailedWithError(error.message)
// Tests never run
// 2. Application crashes during tests (unhandled exception)
FUNCTION handleRuntimeCrash(error):
IF this.killed:
RETURN // Expected termination, ignore error
LOG_ERROR("Application crashed during test execution:", error)
// Current test fails, remaining tests may continue
// 3. Application hangs (initialization never completes)
FUNCTION handleHang(initDelay):
// initDelay timeout expires, but app not responsive
LOG_WARNING("Application may not be fully initialized")
// Tests proceed anyway (may fail due to app not ready)