DEV Community

Cover image for Easily Integrate Databend Test Environment with Testcontainers
ksanaka
ksanaka

Posted on

Easily Integrate Databend Test Environment with Testcontainers

Why Testcontainers?

Testcontainers is an open-source library designed to provide disposable, lightweight instances of databases, message brokers, web browsers, or any service that can run within a Docker container.

Key Features:

  • Disposable: Can be discarded after testing is completed.
  • Lightweight: Quick startup with minimal resource usage.
  • Docker-Based: Leverages containerization for isolation.

Common Use Cases:

  • Database Testing: MySQL, PostgreSQL, MongoDB, etc.
  • Message Queue Testing: RabbitMQ, Kafka, etc.
  • Browser Automation Testing
  • Testing Any Containerized Service

Building test cases with Testcontainers helps avoid test environment contamination, ensures consistency, simplifies test configurations, and enhances test reliability. This tool is particularly well-suited for testing scenarios that depend on external services, enabling the rapid creation of isolated test environments.

Integrating Databend Test Environment with Testcontainers

The Databend team has contributed comprehensive support for Databend as a data source in three mainstream programming languages via pull requests to the following repositories: testcontainers-java, testcontainers-go, and testcontainers-rs. This means developers can now effortlessly integrate Databend test environments into projects using these languages.

Image description

Preparation

  • Ensure that Docker is installed in your operating environment.
  • Have the development environments for Java, Go, and Rust set up and ready.

Java
Dependency Configuration

First, create a Java Demo project. For this example, we’ll use Maven. Add the testcontainers and databend-jdbc dependencies to your pom.xml:

<dependencies>
    <dependency>
        <groupId>org.testcontainers</groupId>
        <artifactId>databend</artifactId>
        <version>1.20.4</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>com.databend</groupId>
        <artifactId>databend-jdbc</artifactId>
        <version>0.2.8</version>
    </dependency>
</dependencies>
Enter fullscreen mode Exit fullscreen mode

For Gradle, use:

testImplementation "org.testcontainers:databend:1.20.4"
Enter fullscreen mode Exit fullscreen mode

Creating a Test Class

Create a TestContainerDatabend test class and define a constructor for it:

public class TestContainerDatabend {
    private final DatabendContainer dockerContainer;

    public TestContainerDatabend() {
        dockerContainer = new DatabendContainer("datafuselabs/databend:v1.2.615");
        dockerContainer.withUsername("databend").withPassword("databend").withUrlParam("ssl", "false");
        dockerContainer.start();
    }
}
Enter fullscreen mode Exit fullscreen mode

In this example, the datafuselabs/databend:v1.2.615 Docker image is specified as the container image for starting Databend. You can find other available versions of the image at Docker Hub. The username and password are set for this Databend instance, and the container service is started immediately.

Next, use this service to complete the test case:

......
@Test
public void testSimple() {
    try (Connection connection = DriverManager.getConnection(getJdbcUrl())) {
        DatabendStatement statement = (DatabendStatement) connection.createStatement();
        statement.execute("SELECT 1");
        ResultSet r = statement.getResultSet();
        while (r.next()) {
            int resultSetInt = r.getInt(1);
            System.out.println(resultSetInt);
            assert resultSetInt == 1;
        }
    } catch (Exception e) {
        throw new RuntimeException("Failed to execute statement: ", e);
    }
}

public String getJdbcUrl() {
    return String.format("jdbc:databend://%s:%s@%s:%s/",
            dockerContainer.getUsername(),
            dockerContainer.getPassword(),
            dockerContainer.getHost(),
            dockerContainer.getMappedPort(8000));
}

Enter fullscreen mode Exit fullscreen mode

When you run the test, you’ll see that Testcontainers starts a Databend container service:

Image description

After the test completes, the container is immediately destroyed to free up resources.

In addition to Databend, Testcontainers supports most popular databases, message queues, and other services, enabling you to easily build test suites that rely on these resources. For the complete project code, refer to testcontainers-databend.

Go

If your Golang project requires a Databend service, you can use testcontainers-go. Below is an example:

package main

import (
 "context"
 "database/sql"
 "testing"

 _ "github.com/datafuselabs/databend-go"
 "github.com/stretchr/testify/require"

 "github.com/testcontainers/testcontainers-go"
 "github.com/testcontainers/testcontainers-go/modules/databend"
)

func TestDatabend(t *testing.T) {
 ctx := context.Background()

 ctr, err := databend.Run(ctx, "datafuselabs/databend:v1.2.615")
 testcontainers.CleanupContainer(t, ctr)
 require.NoError(t, err)

 // perform assertions
 connectionString, err := ctr.ConnectionString(ctx, "sslmode=disable")
 require.NoError(t, err)

 mustConnectionString := ctr.MustConnectionString(ctx, "sslmode=disable")
 require.Equal(t, connectionString, mustConnectionString)

 db, err := sql.Open("databend", connectionString)
 require.NoError(t, err)
 defer db.Close()

 err = db.Ping()
 require.NoError(t, err)

 _, err = db.Exec("CREATE TABLE IF NOT EXISTS a_table ( \n" +
  " `col_1` VARCHAR(128) NOT NULL, \n" +
  " `col_2` VARCHAR(128) NOT NULL \n" +
  ")")
 require.NoError(t, err)
}

Enter fullscreen mode Exit fullscreen mode

Rust

Since Databend is written in Rust, you can use testcontainer-rs to quickly spin up a Databend container service in your Rust project. Below is an example:

#[cfg(test)]
mod tests {
    use databend_driver::Client;

    use crate::{databend::Databend as DatabendImage, testcontainers::runners::AsyncRunner};

    #[tokio::test]
    async fn test_databend() {
        let databend = DatabendImage::default().start().await.unwrap();
        let http_port = databend.get_host_port_ipv4(8000).await.unwrap();
        // "databend://user:password@localhost:8000/default?sslmode=disable
        let dsn = format!(
            "databend://databend:databend@localhost:{}/default?sslmode=disable",
            http_port
        );
        let client = Client::new(dsn.to_string());
        let conn = client.get_conn().await.unwrap();
        let row = conn.query_row("select 'hello'").await.unwrap();
        assert!(row.is_some());
        let row = row.unwrap();
        let (val,): (String,) = row.try_into().unwrap();
        assert_eq!(val, "hello");

        let conn2 = conn.clone();
        let row = conn2.query_row("select 'world'").await.unwrap();
        assert!(row.is_some());
        let row = row.unwrap();
        let (val,): (String,) = row.try_into().unwrap();
        assert_eq!(val, "world");
    }
}
Enter fullscreen mode Exit fullscreen mode

Wrap-up

A reliable testing framework and toolchain are essential cornerstones for ensuring code quality. With the enhancement of Databend’s multi-language support for Testcontainers, developers can conduct database-related integration tests more conveniently, thereby improving overall development efficiency and code quality.

Whether using Java, Go, or Rust, Testcontainers provides reliable support for Databend developers’ testing efforts. We look forward to seeing more developers leverage this powerful testing tool in real-world projects to build more robust application systems.

Top comments (0)