YugabyteDB Integration Testing with HikariCP and Testcontainers
Written on
Chapter 1: Introduction to YugabyteDB and Testing Frameworks
This guide outlines the process of establishing a basic integration testing setup for YugabyteDB using Spring Boot and Testcontainers. By harnessing the capabilities of Testcontainers, developers can easily configure Docker images tailored for testing. The primary focus here is on utilizing Testcontainers to verify interactions with the Yugabyte database.
What is YugabyteDB?
According to its official website, YugabyteDB is a widely-used distributed SQL database that provides exceptional scalability, fault tolerance, and data durability. It allows users to deploy databases across multiple data centers and cloud providers, ensuring optimal performance and high availability.
What is Spring Boot?
Spring Boot simplifies the creation of stand-alone, production-ready Spring applications that can be executed with minimal setup.
What is Docker?
Docker is an open-source platform that facilitates the development, shipping, and running of applications. It allows developers to separate applications from their infrastructure, enabling faster software delivery.
What is Testcontainers?
Testcontainers is an open-source framework designed to provide lightweight, temporary instances of databases, message brokers, web browsers, or virtually anything else that can run in a Docker container.
Before We Get Started
To follow along, ensure you have the following installed:
- Docker: Alternatively, you can use a Docker-compatible tool such as Rancher or Podman.
- Git: This is the preferred method for quickly cloning and running the demo.
How It Started
The project commenced with Spring Boot by visiting start.spring.io/ and including the necessary Maven dependencies: Lombok, Testcontainers, and the PostgreSQL Driver.
Getting the Code
The source code can be cloned from GitHub using the following command:
Code Overview
The ConnectionPool Class
The ConnectionPool.java file is a Java class that implements the DbConnection interface, managing database connections through HikariCPβa lightweight and efficient JDBC connection pooling framework developed by Brett Wooldridge.
To utilize HikariCP, include the following dependency in your pom.xml:
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>5.1.0</version>
</dependency>
Import the necessary classes from Hikari and the Java SQL package:
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import java.sql.Connection;
A connection can be established as follows:
HikariConfig config = new HikariConfig();
config.setJdbcUrl(url);
config.setUsername(username);
config.setPassword(password);
config.setMaximumPoolSize(10);
create(config);
The CustomerService Class
The Customer.java class represents a Customer object, while CustomerService.java manages customer-related operations. The CustomerService class has a private variable for dbConnection, with its constructor accepting an instance of the DbConnection interface.
public CustomerService(DBConnection dbConnection) {
this.dbConnection = dbConnection;
}
The getCustomer() and createCustomer() methods utilize the Connection object for selecting and inserting data into the Customer table:
public Customer getCustomer(int id) throws SQLException {
try (Connection connection = this.dbConnectionProvider.getConnection()) {
String query = "SELECT * FROM " + TABLE + " WHERE id = ?";
...
}
}
public Customer createCustomer(Customer customer) throws SQLException {
try (Connection connection = this.dbConnectionProvider.getConnection()) {
String query = "INSERT INTO " + TABLE + " (id, name) VALUES (?, ?)";
...
}
}
The Tests
The tests are defined in the CustomerServiceUsingHikariTest class. The class is annotated with several important annotations:
@SpringBootTest
@Testcontainers
@TestMethodOrder(MethodOrderer.MethodName.class)
class CustomerServiceUsingHikariTest {
The @SpringBootTest annotation indicates the use of Spring Boot testing support. The @Testcontainers annotation integrates Testcontainers to manage Docker containers during testing, while the @TestMethodOrder(MethodOrderer.MethodName.class) annotation specifies that tests should run in alphabetical order based on their names.
A static variable for the YugabyteDB container is defined as follows:
static YugabyteDBYSQLContainer yugabyteDBYSQLContainer = new YugabyteDBYSQLContainer("yugabytedb/yugabyte:latest")
.waitingFor(Wait.defaultWaitStrategy());
This variable represents an instance of the YugabyteDB container from Testcontainers, using the official Docker image.
The remaining variables are initialized in the @BeforeAll method, including creating the connectionProvider, TestUtils, and CustomerService objects:
@BeforeAll
static void startDb() throws SQLException, InterruptedException {
yugabyteDBYSQLContainer.start();
final String url = yugabyteDBYSQLContainer.getJdbcUrl().replace("yugabytedb", "postgresql");
dbConnection = new ConnectionPool(url, yugabyteDBYSQLContainer.getUsername(), yugabyteDBYSQLContainer.getPassword());
testUtils = new TestUtils(dbConnection);
testUtils.setUpData();
customerService = new CustomerService(dbConnection);
}
The three methods annotated with @Test are clear in their purpose:
@Test
public void containerRunning() {
// Verify that the container is operational
assertTrue(yugabyteDBYSQLContainer.isRunning());
}
@Test
public void getCustomer() throws SQLException {
// Generate a random customer using test utilities
Customer testCustomer = testUtils.getRandomCustomer();
// Retrieve the customer from the service
Customer customer = customerService.getCustomer(testCustomer.getId());
// Validate the retrieved customer's ID and name
assertEquals(testCustomer.getId(), customer.getId());
assertEquals(testCustomer.getName(), customer.getName());
}
@Test
public void createCustomer() throws SQLException {
// Create a new Customer object
String testName = testUtils.getRandomString(10);
Customer testCustomer = new Customer(testName);
// Create the customer and verify the result
Customer newCustomer = customerService.createCustomer(testCustomer);
assertNotNull(newCustomer);
// Verify the customer was created correctly
Customer customer = customerService.getCustomer(newCustomer.getId());
assertEquals(testName, customer.getName());
}
Running the Tests
To build the project without executing the tests, run the following command in the project directory:
mvn clean spring-boot:run -DskipTests
Expected output:
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 4.265 s
[INFO] Finished at: 2024-03-23T14:35:00Z
[INFO] ------------------------------------------------------------------------
Now, let's execute only the tests:
mvn test -Dtest="CustomerServiceUsingHikariTest"
Expected output:
[INFO] Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 15.78 s -- in com.example.yugabytedbtestcontainersspringboot.CustomerServiceUsingHikariTest
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 3, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 18.400 s
[INFO] Finished at: 2024-03-23T14:38:50Z
[INFO] ------------------------------------------------------------------------
The tests executed successfully! π π
Monitor the logs carefully to trace the execution, which includes checking Docker version compatibility, initiating container creation, waiting for it to be ready, and ultimately connecting using Hikari.
CustomerServiceUsingHikariTest execution logs:
2024-03-23T14:38:48.479Z INFO 7737 --- [yugabytedb-testcontainers-springboot] : Starting CustomerServiceUsingHikariTest using Java 17.0.8
...
Additionally, to build and run the tests in one command, simply execute:
mvn clean package
Summary
In this guide, we explored how to set up integration testing for YugabyteDB using Spring Boot and Testcontainers. By leveraging Testcontainers, developers can effortlessly configure Docker images for testing purposes. This article emphasized the testing of interactions with a Yugabyte database.
With successful test runs and a clear understanding of the integration process, developers can utilize Spring Boot and Testcontainers for efficient and reliable YugabyteDB integration testing in their projects.
Stay tuned for more experiments!
Additional Readings
References
Thank you for reading! Please clap (50 claps) to help spread the word π. Share this article on social media β and follow me for more stories on Programming, Career, AI, and more. π
Discover how to use HikariCP in your next Spring Boot project with this helpful video tutorial.
Learn all about Spring Boot Hikari Connection Pooling in this comprehensive tutorial on YouTube.