Manage multi-container environments using Docker Compose files with programmatic configuration and per-service wait strategies.
Manages Docker Compose environments with configuration for services, builds, and wait strategies.
/**
* Creates a Docker Compose environment from compose files
* @param composeFilePath - Directory containing compose files
* @param composeFiles - Single file name or array of compose file names
*/
class DockerComposeEnvironment {
constructor(composeFilePath: string, composeFiles: string | string[]);
/**
* Build images before starting services
*/
withBuild(): this;
/**
* Set environment variables for all services
* @param environment - Key-value pairs of environment variables
*/
withEnvironment(environment: Environment): this;
/**
* Load environment variables from file
* @param environmentFile - Path to environment file
*/
withEnvironmentFile(environmentFile: string): this;
/**
* Set compose profiles to activate
* @param profiles - Profile names to enable
*/
withProfiles(...profiles: string[]): this;
/**
* Disable recreation of existing containers
*/
withNoRecreate(): this;
/**
* Set image pull policy for services
* @param pullPolicy - Pull policy configuration
*/
withPullPolicy(pullPolicy: ImagePullPolicy): this;
/**
* Set default wait strategy for all services
* @param waitStrategy - Wait strategy to use
*/
withDefaultWaitStrategy(waitStrategy: WaitStrategy): this;
/**
* Set wait strategy for specific service
* @param containerName - Service name from compose file
* @param waitStrategy - Wait strategy to use for this service
*/
withWaitStrategy(containerName: string, waitStrategy: WaitStrategy): this;
/**
* Set startup timeout for all services
* @param startupTimeoutMs - Timeout in milliseconds
*/
withStartupTimeout(startupTimeoutMs: number): this;
/**
* Set custom project name
* @param projectName - Project name for compose environment
*/
withProjectName(projectName: string): this;
/**
* Set additional docker-compose client options
* @param options - Partial compose client options
*/
withClientOptions(options: Partial<ComposeOptions>): this;
/**
* Start the compose environment
* @param services - Optional array of specific services to start
* @returns Promise resolving to started environment
*/
up(services?: Array<string>): Promise<StartedDockerComposeEnvironment>;
}Usage Examples:
import { DockerComposeEnvironment, Wait } from 'testcontainers';
// Basic compose environment
const environment = await new DockerComposeEnvironment(
'./docker',
'docker-compose.yml'
).up();
// Get individual containers
const web = environment.getContainer('web');
const db = environment.getContainer('postgres');
// Cleanup
await environment.down();
// Advanced configuration with wait strategies
const env = await new DockerComposeEnvironment(
'./infrastructure',
['docker-compose.yml', 'docker-compose.test.yml']
)
.withBuild()
.withEnvironment({
NODE_ENV: 'test',
LOG_LEVEL: 'debug'
})
.withProfiles('test', 'monitoring')
.withWaitStrategy('api', Wait.forLogMessage('Server started on port'))
.withWaitStrategy('postgres', Wait.forLogMessage('database system is ready'))
.withWaitStrategy('redis', Wait.forLogMessage('Ready to accept connections'))
.withStartupTimeout(120000)
.up();
// Start only specific services
const partialEnv = await new DockerComposeEnvironment(
'./docker',
'docker-compose.yml'
)
.withEnvironmentFile('.env.test')
.up(['api', 'database']);Represents a running Docker Compose environment with access to individual containers.
/**
* Running Docker Compose environment
*/
class StartedDockerComposeEnvironment {
/**
* Get container by service name
* @param containerName - Service name from compose file
* @returns Started container instance
*/
getContainer(containerName: string): StartedTestContainer;
/**
* Stop all containers in the environment
* @returns Promise resolving to stopped environment
*/
stop(): Promise<StoppedDockerComposeEnvironment>;
/**
* Stop and remove all containers, networks, and volumes
* @param options - Down options including timeout and volume removal
* @returns Promise resolving to downed environment
*/
down(options?: Partial<ComposeDownOptions>): Promise<DownedDockerComposeEnvironment>;
/**
* Async disposal support for automatic cleanup
*/
[Symbol.asyncDispose](): Promise<void>;
}Usage Examples:
import { DockerComposeEnvironment, Wait } from 'testcontainers';
const environment = await new DockerComposeEnvironment(
'./docker',
'docker-compose.yml'
)
.withWaitStrategy('api', Wait.forHttp('/health', 3000))
.withWaitStrategy('db', Wait.forHealthCheck())
.up();
// Access individual containers
const api = environment.getContainer('api');
const apiHost = api.getHost();
const apiPort = api.getMappedPort(3000);
console.log(`API available at http://${apiHost}:${apiPort}`);
const db = environment.getContainer('db');
const dbPort = db.getMappedPort(5432);
// Execute commands on specific services
const result = await db.exec(['psql', '-U', 'postgres', '-c', 'SELECT version()']);
console.log(result.output);
// Get logs from specific service
const logs = await api.logs({ tail: 50 });
logs.on('data', (line) => console.log('API:', line));
// Cleanup - stop but keep containers
await environment.stop();
// Or cleanup - remove everything
await environment.down({ removeVolumes: true });
// Using async disposal (automatic cleanup)
{
await using environment = await new DockerComposeEnvironment(
'./docker',
'docker-compose.yml'
).up();
// Use environment
const api = environment.getContainer('api');
// ... tests ...
// Automatically cleaned up when scope exits
}Represents a stopped Docker Compose environment that can be fully removed.
/**
* Stopped Docker Compose environment
*/
class StoppedDockerComposeEnvironment {
/**
* Remove all containers, networks, and volumes
* @param options - Down options including timeout and volume removal
* @returns Promise resolving to downed environment
*/
down(options?: Partial<ComposeDownOptions>): Promise<DownedDockerComposeEnvironment>;
}Terminal state representing a fully removed Docker Compose environment.
/**
* Fully removed Docker Compose environment (terminal state)
*/
class DownedDockerComposeEnvironment {
// No operations available - environment is fully cleaned up
}Example showing a full integration test setup with multiple services:
import { DockerComposeEnvironment, Wait } from 'testcontainers';
import { describe, it, before, after } from 'node:test';
import assert from 'node:assert';
describe('E2E Integration Tests', () => {
let environment: StartedDockerComposeEnvironment;
let apiUrl: string;
before(async () => {
// Start full environment with custom configuration
environment = await new DockerComposeEnvironment(
'./infrastructure',
['docker-compose.yml', 'docker-compose.integration.yml']
)
.withBuild()
.withEnvironment({
NODE_ENV: 'test',
DATABASE_URL: 'postgresql://test:test@postgres:5432/testdb',
REDIS_URL: 'redis://redis:6379'
})
.withProfiles('integration')
// Wait strategies for each service
.withWaitStrategy('postgres', Wait.forLogMessage(/database system is ready/))
.withWaitStrategy('redis', Wait.forLogMessage('Ready to accept connections'))
.withWaitStrategy('rabbitmq', Wait.forLogMessage('Server startup complete'))
.withWaitStrategy('api', Wait.forHttp('/health', 3000).forStatusCode(200))
.withWaitStrategy('worker', Wait.forLogMessage('Worker started'))
.withStartupTimeout(180000) // 3 minutes for all services
.up();
// Get API connection details
const api = environment.getContainer('api');
apiUrl = `http://${api.getHost()}:${api.getMappedPort(3000)}`;
});
after(async () => {
// Cleanup with volume removal
await environment.down({ removeVolumes: true, timeout: 30000 });
});
it('should process messages through the full pipeline', async () => {
// Test using the full environment
const response = await fetch(`${apiUrl}/api/messages`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ message: 'test' })
});
assert.strictEqual(response.status, 201);
// Verify worker processed the message
const worker = environment.getContainer('worker');
const logs = await worker.logs({ since: Date.now() - 5000 });
// Check logs for processing confirmation
});
});type Environment = { [key: string]: string };
interface ComposeDownOptions {
timeout?: number;
removeVolumes?: boolean;
}
interface ComposeOptions {
filePath: string;
files: string | string[];
projectName: string;
commandOptions?: string[];
composeOptions?: string[];
environment?: NodeJS.ProcessEnv;
logger?: Logger;
executable?: ComposeExecutableOptions;
}
type ComposeExecutableOptions =
| {
executablePath: string;
options?: string[] | (string | string[])[];
standalone?: never;
}
| {
executablePath?: string;
options?: never;
standalone: true;
};
interface ImagePullPolicy {
shouldPull(): boolean;
}
interface WaitStrategy {
withStartupTimeout(startupTimeoutMs: number): WaitStrategy;
}