Database Testing with Testcontainers and Kotlin Exposed ORM

In this article, we will explore how to use Testcontainers and Exposed, a lightweight ORM framework for Kotlin, to create a controlled environment for testing MySQL Database operations.

Setup a Kotlin project with gradle

First we install the latest gradle version using SDKMAN

sdk install gradle 8.1.1

Then create a new Gradle project using gradle init with Kotlin support

mkdir kotlin-project
cd kotlin-project
gradle init --type kotlin-application --dsl kotlin

Add dependencies to gradle (build.gradle.kts)

  // Exposed
  implementation("", "exposed-core", "0.40.1")
  implementation("", "exposed-dao", "0.40.1")
  implementation("", "exposed-jdbc", "0.40.1")
  // MySQL JDBC Driver

  // Connection pool

  // Test containers

Define the entity using Exposed DAO flavor

Exposed supports DSL (Domain Specific Language) and DAO (Data Access Object). The DAO flavor in Exposed is a lightweight ORM for performing CRUD operations on entities 1.

object Users: LongIdTable() {
    val name = varchar("name", 50)

class User(id: EntityID<Long>) : LongEntity(id) {
    companion object : LongEntityClass<User>(Users)

    var name by

LongIdTable uses an auto-incrementing Long primary key

Use Testcontainers to start MySQL container, and perform CRUD operations on User entity

We use a single test container to improve test speed by avoiding repetitive container startup and shutdown for every test or test-class

object TestDatabase {

    private val mySQLContainer: MySQLContainer<Nothing> = MySQLContainer<Nothing>("mysql:8.0.26").apply {
        start() // Start the container

    init {
        val config = HikariConfig().apply {
            jdbcUrl = mySQLContainer.jdbcUrl
            username = mySQLContainer.username
            password = mySQLContainer.password
            driverClassName = "com.mysql.cj.jdbc.Driver"
            maximumPoolSize = 10
        val dataSource = HikariDataSource(config)

        // This doesn't connect to the database but provides a descriptor for future use
        // In the main app, we would do this on system start up

        // Create the schema
        transaction {

We create a simple test to test the crud operations.

Note: To run the tests on your local machine, you must have Docker installed and running. If you're using a Mac, you can use Docker Desktop for Mac.

class UserRepositoryTest {

    fun setUp() {
        // Use the TestDatabase singleton to initialize the database

    fun crudUser() {
        transaction {
            // Create
            val newUser = {
                name = "Alice"

            // Read
            val retrievedUser = User.findById(
            assertEquals("Alice", retrievedUser?.name)

            // Update
            retrievedUser?.apply {
                name = "Bob"

            val updatedUser = User.findById(
            assertEquals("Bob", updatedUser?.name)

            // Delete
            val deletedUser = User.findById(


We've explored how to efficiently set up and automate Kotlin database testing using Testcontainers and the Exposed framework.

  • Testcontainers allow us to run isolated database instance in Docker containers
  • Exposed, with its lightweight DAO, make it easy to perform CRUD operations.
  • Using a single test container throughout our test suit significantly improves performance.

You can find the complete source code for this tutorial on GitHub

Further Reading