Spring Boot Integratd testcontainers Quick Start Demo

HBLOG
3 min readMay 18, 2024

--

1. What are testcontainers?

Testcontainers is a Java library for creating ephemeral Docker containers for unit testing. It is very useful when we want to avoid using an actual server for testing. According to the official website, more than 50 kinds of components are supported.

Application scenarios

Data Access Layer Integration Testing:

Test your data access layer code with containerized instances of MySQL, PostgreSQL, or Oracle databases, but without complicated setup on the developer’s machine, and testing will always start with a known database state, avoiding the distractions of “junk” data. You can also use any other database type that can be containerized.

Application Integration Testing:

Used to run applications in short-term test mode with dependencies such as databases, message queues, or web servers.

UI/Acceptance Testing:

Automate UI testing using a containerized web browser that is compatible with Selenium. Each test can fetch a new instance of the browser without worrying about browser status, plugin versions, or browser auto-upgrades. You will get a video recording of each test session or test failure.

2. Code engineering

Experiment purpose: Use Testcontainers to test Redis in Spring Boot.

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>springboot-demo</artifactId>
<groupId>com.et</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>testcontainers</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers</artifactId>
<version>1.17.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
</project>

entity

package com.et.testcontainers.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.springframework.data.redis.core.RedisHash;
import java.io.Serializable;
@RedisHash("product")
@AllArgsConstructor
@Data
public class Product implements Serializable {
private String id;
private String name;
private double price;

}

service

package com.et.testcontainers.service;
import com.et.testcontainers.Repository.ProductRepository;
import com.et.testcontainers.entity.Product;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class ProductService {
@Autowired
private ProductRepository productRepository;

public Product getProduct(String id) {
return productRepository.findById(id).orElse(null);
}

public void createProduct(Product product) {
productRepository.save(product);
}
}

Repository

package com.et.testcontainers.Repository;
import com.et.testcontainers.entity.Product;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface ProductRepository extends CrudRepository<Product, String> {
}

appliication.properties

spring.redis.host=127.0.0.1
spring.redis.port=6379

Startup class

package com.et.testcontainers;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}

The above are just some of the key codes, all of which can be found in the repositories below

Code repositories

3. Testing

Write a test class

package com.et.testcontainers;
import com.et.testcontainers.entity.Product;
import com.et.testcontainers.service.ProductService;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.utility.DockerImageName;
import static org.junit.Assert.assertEquals;

@RunWith(SpringRunner.class)
@SpringBootTest(classes = DemoApplication.class)
public class DemoTests {
private static Logger log = LoggerFactory.getLogger(DemoTests.class);
@Autowired
ProductService productService;
@Before
public void before() {
log.info("init some data");
}
@After
public void after(){
log.info("clean some data");
}
@Test
public void execute() {
log.info("your method test Code");
}
static {
GenericContainer<?> redis =
new GenericContainer<>(DockerImageName.parse("redis:5.0.3-alpine")).withExposedPorts(6379);
redis.start();
log.info(redis.getHost());
log.info(redis.getMappedPort(6379).toString());
System.setProperty("spring.redis.host", redis.getHost());
System.setProperty("spring.redis.port", redis.getMappedPort(6379).toString());
}
@Test
public void givenProductCreated_whenGettingProductById_thenProductExistsAndHasSameProperties() {
Product product = new Product("1", "Test Product", 10.0);
productService.createProduct(product);
Product productFromDb = productService.getProduct("1");
assertEquals("1", productFromDb.getId());
assertEquals("Test Product", productFromDb.getName());
assertEquals(10.0, productFromDb.getPrice(),0.001);
}
}

Execute the test cases and pass them all, so that we can test our code out of the real environment and not skip unit tests every time because the environment is wrong

4. References

--

--