or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

container-configuration.mdcontainer-customization.mdcontainer-lifecycle.mddatabase-initialization.mdindex.mdjdbc-connection.mdr2dbc-support.mdurl-parameters-timeouts.md
tile.json

index.mddocs/

Testcontainers MySQL Module

The Testcontainers MySQL module provides a way to create lightweight, throwaway MySQL database instances within Docker containers for integration testing. It enables developers to write tests against real MySQL databases without requiring manual database setup or external infrastructure, ensuring tests are repeatable, isolated, and can run anywhere Docker is available.

Key Information for Agents

Required Dependencies

Core Package:

  • org.testcontainers:mysql:1.21.4 (this package)
  • org.testcontainers:testcontainers (core package, provided transitively)

JDBC Driver (Required Separately):

  • Modern driver: com.mysql:mysql-connector-j:8.x (recommended for MySQL 8.0+)
  • Legacy driver: mysql:mysql-connector-java:5.x (for older applications)
  • Critical: This module does NOT include a JDBC driver; it must be added separately

R2DBC Support (Optional):

  • org.testcontainers:r2dbc:1.21.4 (for reactive database access)
  • io.asyncer:r2dbc-mysql:1.0.0 (R2DBC MySQL driver)

System Requirements:

  • Docker daemon must be running and accessible
  • Java 8 or higher
  • Network access for Docker image pulls (first run)

Core Class: MySQLContainer

Full Class Name: org.testcontainers.containers.MySQLContainer<SELF extends MySQLContainer<SELF>>

Inheritance: Extends JdbcDatabaseContainer<SELF> which extends GenericContainer<SELF>

Type Parameter: SELF is the self-referential type for fluent method chaining

Key Methods (Quick Reference):

  • Constructors: MySQLContainer(String|DockerImageName)
  • Configuration: withDatabaseName(String), withUsername(String), withPassword(String), withConfigurationOverride(String)
  • Lifecycle: start(): void, stop(): void, isRunning(): boolean
  • Connection: getJdbcUrl(): String, createConnection(String): Connection, getDriverClassName(): String
  • Initialization: withInitScript(String), withInitScripts(String...|Iterable<String>)
  • Timeouts: withStartupTimeoutSeconds(int), withConnectTimeoutSeconds(int)
  • URL Parameters: withUrlParam(String, String)
  • Reuse: withReuse(boolean)

Default Configuration Values

Database:

  • Database name: "test"
  • Username: "test"
  • Password: "test"
  • Port: 3306 (mapped to random available host port)

Container:

  • Docker image: Must be specified in constructor (e.g., "mysql:8.0", "mysql:5.7.34")
  • Startup timeout: 120 seconds
  • Connect timeout: 120 seconds
  • Container reuse: false (disabled by default)
  • Network isolation: Each container runs in isolated network by default

JDBC URL:

  • Automatically includes: useSSL=false, allowPublicKeyRetrieval=true
  • Format: jdbc:mysql://host:port/database?useSSL=false&allowPublicKeyRetrieval=true[&urlParams]
  • Host: Typically "localhost" but may differ in complex networking setups
  • Port: Dynamically assigned random port (use getMappedPort(3306) to retrieve)

Driver Detection:

  • Automatically detects: com.mysql.cj.jdbc.Driver (8.x) or com.mysql.jdbc.Driver (5.x)
  • Detection happens at runtime based on classpath contents
  • Throws NoDriverFoundException if neither driver is found

Threading and Concurrency Model

Container Instance:

  • Container operations are synchronous (blocking)
  • start() method blocks until container is ready (throws ContainerLaunchException or IllegalStateException on failure)
  • stop() method blocks until container is stopped
  • Container instances are NOT thread-safe for concurrent modification
  • Multiple containers can run concurrently in separate instances
  • Container configuration (fluent methods) should be called before start()

JDBC Connections:

  • JDBC connections are independent of container lifecycle
  • Each connection is thread-safe per connection instance
  • Multiple connections can be created from the same container
  • Connections must be explicitly closed (use try-with-resources)

R2DBC:

  • R2DBC operations are asynchronous and non-blocking
  • Supports backpressure through Reactive Streams
  • Connection factory is thread-safe

Lifecycle Management

Required Sequence:

  1. Create container instance: new MySQLContainer<>(DockerImageName.parse("mysql:8.0"))
  2. Configure container: .withDatabaseName("testdb").withUsername("user")...
  3. Start container: container.start() (blocks until ready)
  4. Use container: container.getJdbcUrl(), container.createConnection(""), etc.
  5. Stop container: container.stop() or use try-with-resources

AutoCloseable Support:

  • MySQLContainer implements AutoCloseable
  • Use try-with-resources for automatic cleanup: try (MySQLContainer<?> mysql = ...) { ... }
  • Container is automatically stopped when exiting try block, even on exceptions

Startup Process:

  1. Docker image is pulled if not already present locally
  2. Container is created with configured settings
  3. Container is started
  4. Liveness check begins (executes test query: "SELECT 1")
  5. Initialization scripts are executed (if configured via withInitScript())
  6. Container is marked as ready when all checks pass

Shutdown Process:

  • Container is stopped gracefully
  • Container is removed after stopping
  • Any data stored in the container is lost (unless persisted externally)
  • Network connections are closed

Exception Handling

ContainerLaunchException:

  • Thrown when container fails to start
  • Common causes: empty password with non-root user, invalid Docker image name, insufficient Docker resources, port conflicts, invalid configuration override path
  • Action: Check configuration, verify Docker resources, check container logs

IllegalStateException:

  • Thrown when container starts but cannot be accessed via JDBC within timeout period
  • Common causes: network connectivity issues, firewall blocking container ports, container internal failures, insufficient resources, database not ready within timeout
  • Action: Increase timeouts, enable logging, verify Docker resources, check network connectivity

NoDriverFoundException:

  • Thrown by getJdbcDriverInstance() or createConnection() if MySQL JDBC driver is not found on classpath
  • Action: Add MySQL JDBC driver dependency (com.mysql:mysql-connector-j or mysql:mysql-connector-java)

SQLException:

  • Thrown by createConnection() if connection cannot be established within configured timeout period
  • Common causes: container not started, network connectivity issues, invalid credentials, database not ready
  • Action: Ensure container is started, increase connection timeout, verify credentials

Common Patterns and Best Practices

Basic Usage Pattern:

try (MySQLContainer<?> mysql = new MySQLContainer<>(DockerImageName.parse("mysql:8.0"))
        .withDatabaseName("testdb")) {
    mysql.start();
    // Use container
    String jdbcUrl = mysql.getJdbcUrl();
}

JUnit 5 Integration:

@Testcontainers
public class MySQLTest {
    @Container
    static MySQLContainer<?> mysql = new MySQLContainer<>(DockerImageName.parse("mysql:8.0"));
    // Container automatically started/stopped by framework
}

Spring Boot Integration:

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

Container Reuse (Development Only):

MySQLContainer<?> mysql = new MySQLContainer<>(DockerImageName.parse("mysql:8.0"))
    .withReuse(true); // Requires testcontainers.reuse.enable=true in ~/.testcontainers.properties

Important Constraints and Limitations

Configuration Immutability:

  • Container configuration cannot be changed after start() is called
  • All fluent configuration methods must be called before start()
  • Database name, username, password are set at container creation time

Password Constraints:

  • Empty passwords are only allowed when using username "root" with MYSQL_ROOT_HOST environment variable set to "%"
  • Empty passwords with non-root usernames will throw ContainerLaunchException
  • Default password is "test" if not specified

Initialization Scripts:

  • Scripts must be on the classpath (typically src/test/resources/)
  • Scripts execute after container starts but before it's marked ready
  • Script failures cause container startup to fail
  • Scripts execute in the order specified

Port Mapping:

  • Port 3306 is automatically exposed and mapped to a random available host port
  • Use getMappedPort(3306) to retrieve the mapped host port
  • Do not assume port numbers; always use getMappedPort() or getJdbcUrl()

Container Reuse:

  • Requires testcontainers.reuse.enable=true in ~/.testcontainers.properties
  • Reused containers persist data between test runs
  • Use with caution: ensure tests clean up data or use separate databases
  • Not recommended for CI/CD pipelines (ensure clean state)

Package Information

  • Package Name: mysql
  • Package Type: maven
  • Group ID: org.testcontainers
  • Artifact ID: mysql
  • Version: 1.21.4
  • Language: Java
  • Installation:
    • Gradle: testImplementation "org.testcontainers:mysql:1.21.4"
    • Maven:
      <dependency>
          <groupId>org.testcontainers</groupId>
          <artifactId>mysql</artifactId>
          <version>1.21.4</version>
          <scope>test</scope>
      </dependency>

Important: This module does not automatically include a MySQL JDBC driver. You must add a MySQL JDBC driver dependency to your project (e.g., com.mysql:mysql-connector-j or mysql:mysql-connector-java).

Core Imports

import org.testcontainers.containers.MySQLContainer;
import org.testcontainers.utility.DockerImageName;

For R2DBC (reactive) support:

import org.testcontainers.containers.MySQLR2DBCDatabaseContainer;
import org.testcontainers.containers.MySQLR2DBCDatabaseContainerProvider;
import io.r2dbc.spi.ConnectionFactoryOptions;

Quick Start Example

import org.testcontainers.containers.MySQLContainer;
import org.testcontainers.utility.DockerImageName;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;

// Create and start a MySQL container
try (MySQLContainer<?> mysql = new MySQLContainer<>(DockerImageName.parse("mysql:8.0"))) {
    mysql.start();

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

    // Create a JDBC connection and run queries
    try (Connection conn = mysql.createConnection("")) {
        Statement stmt = conn.createStatement();
        ResultSet rs = stmt.executeQuery("SELECT 1");
        rs.next();
        int result = rs.getInt(1);
        // result == 1
    }
}

Architecture

The MySQL module is built on the Testcontainers framework and provides the following key components:

  • MySQLContainer: Main container class extending JdbcDatabaseContainer, providing MySQL-specific configuration and lifecycle management
  • Container Lifecycle: Automatic Docker container management (pull, start, stop, remove) with try-with-resources support
  • JDBC Integration: Built-in JDBC URL construction, driver detection, and connection creation
  • R2DBC Support: Reactive database access through MySQLR2DBCDatabaseContainer adapter
  • Configuration Override: Custom my.cnf file mounting for server configuration
  • Initialization Scripts: SQL script execution on container startup

The container automatically:

  • Detects and uses the appropriate MySQL JDBC driver (legacy 5.x or modern 8.x)
  • Adds secure testing parameters to JDBC URLs (useSSL=false, allowPublicKeyRetrieval=true)
  • Manages database creation with configurable name, username, and password
  • Handles MySQL root user password configuration

Capabilities

Container Creation and Configuration

Create and configure MySQL containers with custom settings including Docker image version, database name, credentials, and server configuration.

class MySQLContainer<SELF extends MySQLContainer<SELF>> extends JdbcDatabaseContainer<SELF> {
    // Constructors
    MySQLContainer(String dockerImageName);
    MySQLContainer(DockerImageName dockerImageName);

    // Configuration methods (fluent interface, return SELF)
    SELF withDatabaseName(String databaseName);
    SELF withUsername(String username);
    SELF withPassword(String password);
    SELF withConfigurationOverride(String configPath);
    
    // Getters
    String getDatabaseName(); // default: "test"
    String getUsername(); // default: "test"
    String getPassword(); // default: "test"
}

Container Configuration

JDBC Connection Management

Obtain JDBC connection details and create database connections for executing SQL queries in tests.

class MySQLContainer<SELF extends MySQLContainer<SELF>> {
    // Connection information
    String getJdbcUrl(); // Returns: jdbc:mysql://host:port/database?useSSL=false&allowPublicKeyRetrieval=true[&urlParams]
    String getDriverClassName(); // Returns: "com.mysql.cj.jdbc.Driver" (8.x) or "com.mysql.jdbc.Driver" (5.x)
    String getDatabaseName();
    String getUsername();
    String getPassword();
    String getTestQueryString(); // Returns: "SELECT 1"

    // Connection creation (inherited from JdbcDatabaseContainer)
    Connection createConnection(String queryString) throws SQLException;
    Driver getJdbcDriverInstance() throws NoDriverFoundException;
}

JDBC Connection Management

Database Initialization

Execute SQL scripts to set up database schema and test data when the container starts.

class MySQLContainer<SELF extends MySQLContainer<SELF>> {
    // Inherited from JdbcDatabaseContainer
    SELF withInitScript(String initScriptPath);
    SELF withInitScripts(String... initScriptPaths);
    SELF withInitScripts(Iterable<String> initScriptPaths);
}

Database Initialization

Container Lifecycle Management

Control container startup, shutdown, and monitor container state.

class MySQLContainer<SELF extends MySQLContainer<SELF>> {
    // Inherited from GenericContainer
    void start(); // Blocks until container is ready, throws ContainerLaunchException or IllegalStateException
    void stop(); // Stops and removes container
    boolean isRunning(); // Returns current running state

    // Configuration
    SELF withReuse(boolean reuse); // Enable container reuse (requires testcontainers.reuse.enable=true)
}

Container Lifecycle

URL Parameters and Timeouts

Customize JDBC URL parameters and configure startup/connection timeouts.

class MySQLContainer<SELF extends MySQLContainer<SELF>> {
    // Inherited from JdbcDatabaseContainer
    SELF withUrlParam(String paramName, String paramValue);
    SELF withStartupTimeoutSeconds(int startupTimeoutSeconds); // default: 120
    SELF withConnectTimeoutSeconds(int connectTimeoutSeconds); // default: 120
}

URL Parameters and Timeouts

R2DBC Reactive Support

Use MySQL containers with R2DBC for reactive, non-blocking database access.

class MySQLR2DBCDatabaseContainer implements R2DBCDatabaseContainer {
    MySQLR2DBCDatabaseContainer(MySQLContainer<?> container);

    static ConnectionFactoryOptions getOptions(MySQLContainer<?> container);
    ConnectionFactoryOptions configure(ConnectionFactoryOptions options);

    // Lifecycle methods (delegated to wrapped MySQLContainer)
    void start();
    void stop();
}

class MySQLR2DBCDatabaseContainerProvider implements R2DBCDatabaseContainerProvider {
    static final String DRIVER = "mysql";
    boolean supports(ConnectionFactoryOptions options);
    R2DBCDatabaseContainer createContainer(ConnectionFactoryOptions options);
    ConnectionFactoryMetadata getMetadata(ConnectionFactoryOptions options);
}

R2DBC Reactive Support

Container Customization

Advanced container customization including environment variables, command overrides, port mapping, and logging.

class MySQLContainer<SELF extends MySQLContainer<SELF>> {
    // Inherited from GenericContainer
    SELF withEnv(String key, String value);
    SELF withEnv(Map<String, String> env);
    SELF withCommand(String... commandParts);
    SELF withExposedPorts(Integer... ports);
    SELF withLogConsumer(Consumer<OutputFrame> consumer);

    // Information methods
    String getHost(); // Returns: "localhost" typically
    Integer getMappedPort(int originalPort); // Returns mapped host port for container port
    List<Integer> getExposedPorts();
    Set<Integer> getLivenessCheckPortNumbers();
}

Container Customization

Constants

class MySQLContainer<SELF extends MySQLContainer<SELF>> {
    static final String NAME = "mysql";
    static final Integer MYSQL_PORT = 3306;
}

Default Values

  • Default database name: "test"
  • Default username: "test"
  • Default password: "test"
  • Default port: 3306
  • Startup timeout: 120 seconds
  • Connect timeout: 120 seconds

Error Handling

The module throws the following exceptions:

  • ContainerLaunchException: Thrown when the container fails to start, such as when using an empty password with a non-root user, invalid Docker image name, insufficient Docker resources, or port conflicts
  • NoDriverFoundException: Thrown when the MySQL JDBC driver is not found on the classpath (by getJdbcDriverInstance() or createConnection())
  • IllegalStateException: Thrown when the container starts but cannot be accessed via JDBC within the timeout period (typically network issues, firewall blocking, container internal failures, or insufficient resources)
  • SQLException: Thrown by createConnection() if the connection cannot be established within the configured timeout period

Advanced Features

Container Provider (Internal Usage)

For advanced use cases, the module provides a factory class for creating MySQL containers. This is primarily used internally by Testcontainers for JDBC URL-based container instantiation.

class MySQLContainerProvider extends JdbcDatabaseContainerProvider {
    boolean supports(String databaseType); // Returns true for "mysql"
    JdbcDatabaseContainer newInstance();
    JdbcDatabaseContainer newInstance(String tag);
    JdbcDatabaseContainer newInstance(ConnectionUrl connectionUrl);
}

Note: Most users should use MySQLContainer directly rather than MySQLContainerProvider. The provider is used internally by the Testcontainers JDBC support for connection strings like jdbc:tc:mysql:8.0:///testdb.

Common Usage Patterns

JUnit 5 Integration

import org.junit.jupiter.api.Test;
import org.testcontainers.containers.MySQLContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import org.testcontainers.utility.DockerImageName;

@Testcontainers
public class MySQLJUnit5Test {
    @Container
    private static MySQLContainer<?> mysql = new MySQLContainer<>(DockerImageName.parse("mysql:8.0"))
        .withDatabaseName("testdb");

    @Test
    public void testDatabase() {
        // Container is automatically started before tests and stopped after
        String jdbcUrl = mysql.getJdbcUrl();
        // Run test...
    }
}

Spring Boot Integration

import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
import org.testcontainers.containers.MySQLContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import org.testcontainers.utility.DockerImageName;

@SpringBootTest
@Testcontainers
public class SpringBootMySQLTest {
    @Container
    static MySQLContainer<?> mysql = new MySQLContainer<>(DockerImageName.parse("mysql:8.0"))
        .withDatabaseName("springtest");

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

    // Spring Boot tests...
}

Shared Container Pattern

public abstract class AbstractMySQLTest {
    protected static final MySQLContainer<?> MYSQL_CONTAINER;

    static {
        MYSQL_CONTAINER = new MySQLContainer<>(DockerImageName.parse("mysql:8.0"))
            .withDatabaseName("testdb")
            .withReuse(true);
        MYSQL_CONTAINER.start();
    }

    protected String getJdbcUrl() {
        return MYSQL_CONTAINER.getJdbcUrl();
    }
}