or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

index.mdjdbc-container.mdjdbc-provider.mdr2dbc-support.md
tile.json

tessl/maven-org-testcontainers--clickhouse

Testcontainers module for ClickHouse - provides lightweight, throwaway instances of ClickHouse database for testing

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
mavenpkg:maven/org.testcontainers/clickhouse@1.21.x

To install, run

npx @tessl/cli install tessl/maven-org-testcontainers--clickhouse@1.21.0

index.mddocs/

Testcontainers ClickHouse Module

Testcontainers module for ClickHouse provides lightweight, throwaway instances of ClickHouse database containers for testing Java applications. It enables developers to create reproducible test environments with real ClickHouse databases, ensuring tests run in isolation with automatic container lifecycle management and proper cleanup.

Key Information for Agents

Required Dependencies:

  • org.testcontainers:clickhouse (this package, version 1.21.4)
  • org.testcontainers:testcontainers core package is required (provided transitively)
  • ClickHouse JDBC driver must be added separately (see Driver Compatibility section)
  • Docker daemon must be running and accessible
  • Java 8 or higher

Core Capabilities:

  • ClickHouseContainer - Main JDBC-based container implementation extending JdbcDatabaseContainer
  • ClickHouseProvider - Service provider enabling JDBC URL-based container instantiation (e.g., jdbc:tc:clickhouse://)
  • ClickHouseR2DBCDatabaseContainer - R2DBC wrapper for reactive database connectivity
  • ClickHouseR2DBCDatabaseContainerProvider - R2DBC provider for URL-based instantiation
  • Automatic health checking via HTTP endpoint (port 8123)
  • Automatic driver detection from classpath
  • Support for initialization scripts
  • Container reuse support
  • Custom credentials, database names, and URL parameters

Key Interfaces and Classes:

  • ClickHouseContainer - Main container class: new ClickHouseContainer(String dockerImageName), start(), stop(), getJdbcUrl(), getDriverClassName(), getUsername(), getPassword(), getDatabaseName(), withUsername(), withPassword(), withDatabaseName(), withUrlParam(), withInitScript(), withInitScripts(), withStartupTimeoutSeconds(), withConnectTimeoutSeconds(), withReuse(), withEnv(), getMappedPort(), getHost()
  • ClickHouseProvider - Service provider: supports(String databaseType), newInstance(), newInstance(String tag)
  • ClickHouseR2DBCDatabaseContainer - R2DBC wrapper: new ClickHouseR2DBCDatabaseContainer(ClickHouseContainer), getOptions(ClickHouseContainer), configure(ConnectionFactoryOptions), start(), stop()
  • ClickHouseR2DBCDatabaseContainerProvider - R2DBC provider: supports(ConnectionFactoryOptions), createContainer(ConnectionFactoryOptions), getMetadata(ConnectionFactoryOptions)

Default Behaviors:

  • Default Docker image: clickhouse/clickhouse-server:24.12-alpine (must be specified in constructor)
  • Default username: test
  • Default password: test
  • Default database: default
  • Default HTTP port: 8123 (database access and health checks, mapped to random host port)
  • Default native port: 9000 (console access, mapped to random host port)
  • Default wait strategy: HTTP check on port 8123 expecting "Ok." response with status 200
  • Default startup timeout: 120 seconds (2 minutes)
  • Default connection timeout: 120 seconds (2 minutes)
  • Container reuse: Not enabled by default (new container per test)
  • Network isolation: Each container runs in isolated network by default
  • Health check: HTTP GET request to port 8123, expects "Ok." response
  • Driver detection: Automatically detects available JDBC driver from classpath (priority: com.clickhouse.jdbc.Driver > com.clickhouse.jdbc.ClickHouseDriver)
  • JDBC URL format: jdbc:clickhouse://host:port/database
  • R2DBC URL format: r2dbc:tc:clickhouse:///database?parameters
  • Testcontainers JDBC URL format: jdbc:tc:clickhouse://host/database?parameters
  • Environment variables: CLICKHOUSE_DB, CLICKHOUSE_USER, CLICKHOUSE_PASSWORD automatically set

Threading Model:

  • Container instances are not thread-safe for concurrent modification
  • start() and stop() methods are blocking and should be called from a single thread
  • Multiple containers can run concurrently in separate instances (each gets unique ports)
  • JDBC connections are independent of container lifecycle and thread safety
  • R2DBC connections are independent of container lifecycle and thread safety
  • Connection factory operations (R2DBC) return reactive types (Mono, Flux) and are non-blocking
  • Container lifecycle methods (start(), stop()) are blocking even for R2DBC wrappers
  • Multiple reactive streams can use the same connection factory concurrently
  • Connection factory is thread-safe

Lifecycle:

  • Container must be started with start() before use (blocking call)
  • Container automatically waits for health check to pass before start() returns
  • Container must be stopped with stop() or use try-with-resources for automatic cleanup
  • Container stops automatically when using try-with-resources syntax
  • Container data is ephemeral (lost when container stops unless using volumes)
  • Initialization scripts are executed after health check passes but before start() returns
  • Scripts are executed sequentially in the order provided
  • Each script is executed as a single transaction (if supported by ClickHouse)
  • Script failures cause start() to throw ContainerLaunchException
  • Container reuse: When enabled, containers persist across test runs if configuration matches
  • Reused containers are not stopped when test completes
  • Reused containers persist data between test runs
  • Container reuse requires testcontainers.reuse.enable=true in ~/.testcontainers.properties
  • R2DBC wrapper does not start container automatically (must call start() on wrapper or wrapped container)
  • R2DBC wrapper delegates lifecycle methods to wrapped container

Exceptions:

  • IllegalStateException: Thrown if container methods are called in wrong order (e.g., getJdbcUrl() before start())
  • ContainerLaunchException: Thrown if container fails to start (Docker issues, image pull failures, health check timeouts, initialization script failures)
  • TimeoutException: Thrown if startup timeout is exceeded
  • SQLException: Thrown by JDBC operations (connection failures, query errors)
  • ClassNotFoundException: Thrown if no JDBC driver is found on classpath when getDriverClassName() is called
  • IllegalArgumentException: Thrown for invalid parameters (null/empty image name, null/empty username/database name, invalid timeout values, null parameter names/values)
  • EvaluationException: Thrown by SpEL expression evaluation (if used in custom configurations)
  • Port conflicts: Rare, but can occur if all ports are exhausted (Testcontainers handles automatically)
  • Network errors from R2DBC: Connection failures, authentication errors
  • Reactive stream errors: Errors in R2DBC operations propagate through reactive streams

Edge Cases:

  • Image name without tag defaults to latest (not recommended for tests)
  • Invalid image names will fail at start() time, not construction time
  • Image pull failures are reported as ContainerLaunchException during start()
  • Empty strings for username/database name throw IllegalArgumentException
  • Null values throw IllegalArgumentException for required parameters
  • Credentials are validated when container starts, not when set
  • Special characters in credentials are URL-encoded in JDBC URL
  • Multiple calls to withUrlParam with same name: last value wins
  • Special characters in URL parameter values are automatically URL-encoded
  • Missing script files cause ContainerLaunchException during start()
  • Empty scripts are allowed (no-op)
  • Scripts with syntax errors cause ContainerLaunchException
  • Very large scripts may exceed connection timeout (increase with withConnectTimeoutSeconds())
  • Startup timeout includes: image pull, container creation, container start, health check
  • Connection timeout is used for: health check connection, initialization script execution
  • Very short timeouts (< 10 seconds) may cause false failures on slow systems
  • Multiple start() calls throw IllegalStateException
  • Calling methods before start() throws IllegalStateException
  • Invalid port numbers throw IllegalArgumentException
  • Container reuse enabled but no matching container: new container is created
  • Container reuse enabled but property not set: reuse is silently ignored, new container created
  • Reused containers may have stale data from previous test runs
  • Null key in withEnv() throws IllegalArgumentException
  • Null value in withEnv() sets environment variable to empty string
  • Environment variables override container defaults (e.g., CLICKHOUSE_USER, CLICKHOUSE_PASSWORD)
  • Environment variables are set before container starts, cannot be changed after
  • Driver detection: First checks for com.clickhouse.jdbc.Driver (new driver), falls back to com.clickhouse.jdbc.ClickHouseDriver (legacy driver)
  • JDBC URL-based instantiation: Invalid URLs throw SQLException with descriptive message
  • JDBC URL-based instantiation: Missing image tags cause ContainerLaunchException during container start
  • JDBC URL-based instantiation: Invalid parameters are ignored (no error, parameter is skipped)
  • JDBC URL-based instantiation: Username and password from JDBC URL are ignored (always uses container defaults)
  • R2DBC: Null container throws IllegalArgumentException
  • R2DBC: Container must be started before R2DBC operations (not enforced at construction)
  • R2DBC: Options are immutable (modifications return new instance)
  • R2DBC: Existing options in input are preserved, container options override
  • R2DBC: Missing R2DBC module on classpath causes ClassNotFoundException when creating connection factory
  • R2DBC: Container not started throws IllegalStateException when accessing port/host
  • R2DBC: Invalid R2DBC URLs throw IllegalArgumentException or use wrong provider
  • R2DBC: Container stops while connection is active causes connection errors in reactive streams
  • Port conflicts: Testcontainers automatically finds available ports, but conflicts can occur in CI/CD
  • Health check timeouts: Slow container startup may exceed timeout (adjust with withStartupTimeoutSeconds())
  • Multiple containers: Each container gets unique ports; coordinate if testing multi-container scenarios
  • Network isolation: Containers cannot communicate by default; use withNetwork() and withNetworkAliases() for multi-container tests
  • Resource cleanup: Always stop containers even if tests fail; use try-with-resources or finally blocks
  • Concurrent tests: Each test should use separate container instances to avoid port conflicts
  • CI/CD environments: May require Docker-in-Docker or remote Docker configuration
  • Connection URL encoding: Special characters in credentials and parameters are automatically URL-encoded
  • Container restart: Data persists only if volumes are used; otherwise data is lost
  • Client connection timing: Wait for start() to return before connecting clients; container may need additional time to be fully ready
  • File system permissions: Initialization scripts must be readable from classpath
  • Initial data loading: Happens during startup; large files may slow container start
  • ClickHouse version compatibility: Different ClickHouse versions may have different SQL syntax; verify image version matches requirements
  • Driver compatibility: New driver (com.clickhouse.jdbc.Driver) recommended for ClickHouse versions 20.7 and later
  • Legacy driver: com.clickhouse.jdbc.ClickHouseDriver supported for backward compatibility
  • URL parameters: clickhouse_setting_* prefix required for ClickHouse session settings
  • Testcontainers URL parameters: TC_IMAGE_TAG, TC_REUSABLE, TC_INITSCRIPT are case-sensitive
  • R2DBC URL parameters: Same as JDBC URL parameters, but uses r2dbc:tc: prefix
  • ServiceLoader configuration: Provider is automatically discovered via Java's ServiceLoader mechanism
  • Container matching for reuse: Based on image name, username, password, database name, init scripts

Package Information

  • Package Name: org.testcontainers:clickhouse

  • Package Type: maven

  • Language: Java

  • Installation:

    Maven:

    <dependency>
        <groupId>org.testcontainers</groupId>
        <artifactId>clickhouse</artifactId>
        <version>1.21.4</version>
        <scope>test</scope>
    </dependency>

    Gradle:

    testImplementation "org.testcontainers:clickhouse:1.21.4"

Important: This module does not automatically include a ClickHouse JDBC driver. You must add one of the following drivers as a dependency:

<!-- New driver (recommended) -->
<dependency>
    <groupId>com.clickhouse</groupId>
    <artifactId>clickhouse-jdbc</artifactId>
    <version>0.7.2</version>
    <classifier>http</classifier>
    <scope>test</scope>
</dependency>

Core Imports

import org.testcontainers.clickhouse.ClickHouseContainer;
import org.testcontainers.utility.DockerImageName;

For R2DBC support:

import org.testcontainers.clickhouse.ClickHouseR2DBCDatabaseContainer;
import io.r2dbc.spi.ConnectionFactoryOptions;

Basic Usage

import org.testcontainers.clickhouse.ClickHouseContainer;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;

// Create and start a ClickHouse container
try (ClickHouseContainer clickhouse = new ClickHouseContainer("clickhouse/clickhouse-server:24.12-alpine")) {
    clickhouse.start();

    // Get connection details
    String jdbcUrl = clickhouse.getJdbcUrl();
    String username = clickhouse.getUsername();
    String password = clickhouse.getPassword();

    // Connect and execute queries
    try (Connection conn = DriverManager.getConnection(jdbcUrl, username, password);
         Statement stmt = conn.createStatement();
         ResultSet rs = stmt.executeQuery("SELECT 1")) {
        rs.next();
        System.out.println("Result: " + rs.getInt(1));
    }
}

Architecture

The ClickHouse Testcontainers module is built around several key components:

  • ClickHouseContainer: Main JDBC-based container implementation extending JdbcDatabaseContainer, providing ClickHouse-specific configuration and automatic health checking via HTTP endpoint
  • ClickHouseProvider: Service provider enabling JDBC URL-based container instantiation (e.g., jdbc:tc:clickhouse://)
  • R2DBC Support: Optional reactive database connectivity through ClickHouseR2DBCDatabaseContainer and ClickHouseR2DBCDatabaseContainerProvider
  • Container Lifecycle: Automatic container startup, health checking, and cleanup inherited from Testcontainers core
  • Port Management: Exposes both HTTP port (8123) for database access and native port (9000) for console access

Capabilities

JDBC Container

Core JDBC-based ClickHouse container with automatic configuration, lifecycle management, and health checking. Supports custom credentials, database names, initialization scripts, and URL parameters.

public class ClickHouseContainer extends JdbcDatabaseContainer<ClickHouseContainer> {
    // Constructors
    public ClickHouseContainer(String dockerImageName);
    public ClickHouseContainer(DockerImageName dockerImageName);

    // Connection information
    public String getJdbcUrl();
    public String getDriverClassName();
    public String getUsername();
    public String getPassword();
    public String getDatabaseName();
    public String getTestQueryString();

    // Configuration methods
    public ClickHouseContainer withUsername(String username);
    public ClickHouseContainer withPassword(String password);
    public ClickHouseContainer withDatabaseName(String databaseName);
    public ClickHouseContainer withUrlParam(String paramName, String paramValue);
    public ClickHouseContainer withInitScript(String initScriptPath);
    public ClickHouseContainer withInitScripts(String... initScriptPaths);
    public ClickHouseContainer withInitScripts(Iterable<String> initScriptPaths);
    public ClickHouseContainer withStartupTimeoutSeconds(int startupTimeoutSeconds);
    public ClickHouseContainer withConnectTimeoutSeconds(int connectTimeoutSeconds);
    public ClickHouseContainer withReuse(boolean reuse);
    public ClickHouseContainer withEnv(String key, String value);

    // Lifecycle methods
    public void start();
    public void stop();
    public Integer getMappedPort(int originalPort);
    public String getHost();
    public Set<Integer> getLivenessCheckPortNumbers();
}

JDBC Container API

JDBC URL-Based Instantiation

Service provider for creating ClickHouse containers directly from JDBC URLs, enabling seamless integration with JDBC connection pools and frameworks.

public class ClickHouseProvider extends JdbcDatabaseContainerProvider {
    public boolean supports(String databaseType);
    public JdbcDatabaseContainer<?> newInstance();
    public JdbcDatabaseContainer<?> newInstance(String tag);
}

JDBC Provider API

R2DBC Support

Reactive database connectivity for ClickHouse containers, providing non-blocking database access through R2DBC. Supports both programmatic and URL-based instantiation patterns.

public class ClickHouseR2DBCDatabaseContainer implements R2DBCDatabaseContainer {
    public ClickHouseR2DBCDatabaseContainer(ClickHouseContainer container);
    public static ConnectionFactoryOptions getOptions(ClickHouseContainer container);
    public void start();
    public void stop();
    public ConnectionFactoryOptions configure(ConnectionFactoryOptions options);
}

public class ClickHouseR2DBCDatabaseContainerProvider implements R2DBCDatabaseContainerProvider {
    public boolean supports(ConnectionFactoryOptions options);
    public R2DBCDatabaseContainer createContainer(ConnectionFactoryOptions options);
    public ConnectionFactoryMetadata getMetadata(ConnectionFactoryOptions options);
}

R2DBC Support API

Container Configuration

Default Settings

  • Docker Image: clickhouse/clickhouse-server
  • Default Tag: 24.12-alpine
  • HTTP Port: 8123 (database access and health checks)
  • Native Port: 9000 (console access)
  • Default Username: test
  • Default Password: test
  • Default Database: default
  • Wait Strategy: HTTP check on port 8123 expecting "Ok." response with status 200
  • Startup Timeout: 120 seconds (2 minutes)
  • Connection Timeout: 120 seconds (2 minutes)

Exposed Ports

The container exposes two ports:

  • 8123: HTTP interface for database access, health checks, and monitoring
  • 9000: Native protocol port for ClickHouse console and native client connections

Environment Variables

The container automatically configures the following environment variables:

  • CLICKHOUSE_DB: Database name (default: "default")
  • CLICKHOUSE_USER: Username (default: "test")
  • CLICKHOUSE_PASSWORD: Password (default: "test")

Driver Compatibility

The module automatically detects and supports multiple ClickHouse JDBC drivers:

  1. New driver (preferred): com.clickhouse.jdbc.Driver - Recommended for ClickHouse versions 20.7 and later
  2. Legacy v1 driver: com.clickhouse.jdbc.ClickHouseDriver - Supported for backward compatibility

Driver selection is automatic based on classpath availability. The new driver is recommended for ClickHouse versions 20.7 and later.

Driver Detection Logic:

  • First checks for com.clickhouse.jdbc.Driver (new driver)
  • Falls back to com.clickhouse.jdbc.ClickHouseDriver (legacy driver)
  • Throws ClassNotFoundException if neither driver is found

Testing Best Practices

Resource Management

Always use try-with-resources to ensure proper cleanup:

try (ClickHouseContainer container = new ClickHouseContainer("clickhouse/clickhouse-server:24.12-alpine")) {
    container.start();
    // Use container...
} // Container automatically stopped

Container Reuse for Performance

Enable container reuse to speed up test execution (containers persist across test runs):

try (ClickHouseContainer container = new ClickHouseContainer("clickhouse/clickhouse-server:24.12-alpine")
        .withReuse(true)) {
    container.start();
    // Container will be reused if configuration matches
}

Important: Container reuse requires testcontainers.reuse.enable=true in ~/.testcontainers.properties.

Parallel Test Execution

Multiple containers can run in parallel (each gets unique ports):

// Safe to run in parallel - each container gets unique ports
@Test
public void test1() {
    try (ClickHouseContainer c1 = new ClickHouseContainer("clickhouse/clickhouse-server:24.12-alpine")) {
        c1.start();
        // Test 1...
    }
}

@Test
public void test2() {
    try (ClickHouseContainer c2 = new ClickHouseContainer("clickhouse/clickhouse-server:24.12-alpine")) {
        c2.start();
        // Test 2...
    }
}

Initialization Scripts

Use initialization scripts for consistent test data:

try (ClickHouseContainer container = new ClickHouseContainer("clickhouse/clickhouse-server:24.12-alpine")
        .withInitScripts("schema.sql", "test-data.sql")) {
    container.start();
    // Schema and data are already loaded
}

Error Handling

Common Exceptions

ContainerLaunchException: Container failed to start

try {
    container.start();
} catch (ContainerLaunchException e) {
    // Handle: Docker not running, image pull failure, health check timeout
    System.err.println("Container failed to start: " + e.getMessage());
}

IllegalStateException: Method called in wrong order

// Wrong: calling getJdbcUrl() before start()
String url = container.getJdbcUrl(); // Throws IllegalStateException

// Correct: start first
container.start();
String url = container.getJdbcUrl(); // OK

TimeoutException: Startup timeout exceeded

try (ClickHouseContainer container = new ClickHouseContainer("clickhouse/clickhouse-server:24.12-alpine")
        .withStartupTimeoutSeconds(30)) {
    container.start(); // May throw TimeoutException if container takes > 30s
} catch (TimeoutException e) {
    // Increase timeout or check Docker performance
}

Health Check Failures

If health check fails, container startup will timeout:

// Health check may fail if:
// 1. ClickHouse takes longer than expected to start
// 2. Port 8123 is not accessible
// 3. ClickHouse returns unexpected response

// Solution: Increase timeout
container.withStartupTimeoutSeconds(300); // 5 minutes

Performance Considerations

Startup Time

  • First startup: ~10-30 seconds (image pull + container start)
  • Subsequent startups: ~5-15 seconds (container start only)
  • With container reuse: ~0 seconds (container already running)

Resource Usage

  • Memory: ~500MB-1GB per container (depends on ClickHouse version)
  • Disk: ~200MB-500MB per container (image size)
  • CPU: Minimal when idle, spikes during queries

Optimization Tips

  1. Use container reuse for faster test execution
  2. Use lighter images (alpine variants) when possible
  3. Share containers across tests when data isolation is not required
  4. Use withStartupTimeoutSeconds() to fail fast on slow environments

Integration Patterns

JUnit 5 Integration

import org.junit.jupiter.api.Test;
import org.testcontainers.clickhouse.ClickHouseContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;

@Testcontainers
public class ClickHouseTest {
    @Container
    static ClickHouseContainer clickhouse = new ClickHouseContainer("clickhouse/clickhouse-server:24.12-alpine");

    @Test
    public void test() {
        String jdbcUrl = clickhouse.getJdbcUrl();
        // Use container...
    }
}

Spring Boot Integration

@SpringBootTest
@Testcontainers
public class SpringBootTest {
    @Container
    static ClickHouseContainer clickhouse = new ClickHouseContainer("clickhouse/clickhouse-server:24.12-alpine");

    @DynamicPropertySource
    static void configureProperties(DynamicPropertyRegistry registry) {
        registry.add("spring.datasource.url", clickhouse::getJdbcUrl);
        registry.add("spring.datasource.username", clickhouse::getUsername);
        registry.add("spring.datasource.password", clickhouse::getPassword);
    }
}

Connection Pool Integration

import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;

try (ClickHouseContainer container = new ClickHouseContainer("clickhouse/clickhouse-server:24.12-alpine")) {
    container.start();
    
    HikariConfig config = new HikariConfig();
    config.setJdbcUrl(container.getJdbcUrl());
    config.setUsername(container.getUsername());
    config.setPassword(container.getPassword());
    
    try (HikariDataSource dataSource = new HikariDataSource(config)) {
        // Use connection pool...
    }
}