Integration test with TestContainer
Tags: container, docker, test, begginer
Published: 2020-02-20 03:32:03

---
title: Integration test with TestContainer
published: true
date: 2020-02-20 02:47:50 UTC
tags: container,docker,test,begginer
canonical_url: https://educosta.dev/blog/integration-test-with-testcontainer/
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/jgv0pfwd82ygpfhg8dpa.png
---

## Test the integration layer of your java application with a real Postgres instance using the TestContainer library

Creating efficient integration tests is [a common challenge](https://dextra.com.br/pt/por-que-devo-testar-minhas-aplicacoes/) when building [microservices](https://dev.to/educostadev/what-is-microservice-461k). Ensure the environment equality among the development and the production environment can be a pain. Mainly when the tools and services require too much hand configuration. When this happens, we usually take a short cut to adopt lightweight tools for the development and test environment. This makes the difference between the environments even bigger. 

The guidelines [Twelve-Factor Apps](http://12factor.net/) tell us that environment equality is one of the success keys to build [cloud-native](https://educosta.dev/blog/what-is-cloud-native/) applications. Developing and testing on similar environments allow us to create resilient applications that bring safety and speed to deploy.  

In this blog post, we will see how to create an integration test using the [Testcontainer](https://www.testcontainers.org/) library. The test will start a PostgreSQL container and run reading and writing tests on a real database instance. 

### What is Testcontainer?

Testcontainer is a java library that gives us support to create unit tests. It provides lightweight and throwaway instances of common databases or any application that ran into a [Docker](https://www.docker.com/) container. It also makes easy the creation of integration tests using container and without using complex configurations 

### Pre-requirements

To run the example code [available on Github](https://github.com/educostadev/poc-testcontainers), you will need to have installed the git and an IDE. Besides that, you also need to have the Docker and be able to perform the following command:

```
docker info
```

If you are using Linux, look that the command does not have the “_sudo_” prefix. You must be able to execute this command without this prefix because during the test running the library will use the _Docker_ to download images and create containers.  

To run the docker command without the “_sudo_” prefix, your user should be added to the docker user group by the following command: 

```bash
sudo usermod -aG docker $USER
```

After running this command you will need to log-off and log-in to the changes take effect. 

### Example project

[This project](https://github.com/educostadev/poc-testcontainers.git) has an integration test that starts a PostgreSQL container, sets up the database initial status running a SQL script, runs the test and in the end, destroys the container.  

To see the code, clone the git repository running the following command:

```bash
git clone https://github.com/educostadev/poc-testcontainers.git
```

Import the project in your IDE as a Maven project. If you use IntelliJ, [this link](https://www.lagomframework.com/documentation/1.6.x/java/IntellijMaven.html) has more information about how to import a Maven project. 

The Testcontainer integrates well with [JUnit4](https://www.testcontainers.org/quickstart/junit_4_quickstart/) and [JUnit5](https://www.testcontainers.org/quickstart/junit_5_quickstart/) using dependencies specified within the pom.xml. Open the pom.xml file from the code example and look at the dependencies responsible for the use of Testcontainer with JUnit5.

```xml
<dependency>
   <groupId>org.testcontainers</groupId>
   <artifactId>junit-jupiter</artifactId>
   <version>1.12.3</version>
   <scope>test</scope>
</dependency>
```

If you are using a database already supported by Testcontainer then you will need to add in your project the correct dependencies. Here in[_ this link_](https://www.testcontainers.org/modules/databases/), you can find a list of databases supported by the Testcontainer. In our example, we are using a PostgreSQL instance, and the following dependency is required:

```xml
<dependency>
   <groupId>org.testcontainers</groupId>
   <artifactId>postgresql</artifactId>
   <scope>test</scope>
</dependency>
```

The project files follow a standard Spring Boot application. Within the application.yml file, you can find all the database connection entries. Pay attention to the use of environment variables named _DB\_URL_, _DB\_USERNAME_, and _DB\_PASSWORD_.

```yaml
server:
  port: 8083
logging:
  level:
    ROOT: info
    org.hibernate.tool.hbm2ddl: debug
    org.hibernate.SQL: debug
    org.hibernate.type.descriptor.sql: trace
spring:
  datasource:
    url: ${DB_URL}
    username: ${DB_USERNAME}
    password: ${DB_PASSWORD}
    driver-class-name: org.postgresql.Driver
    hikari:
      maximum-pool-size: 5
      connection-timeout: 20000
    initialization-mode: always
  jpa:
    database-platform: org.hibernate.dialect.PostgreSQLDialect
    hibernate:
      ddl-auto: create
```

The _DummyDao_ class makes use of the _jdbcTemplate_ library to execute SQL queries in the database. This is the class that will be tested using the _PostgreSQL_ container. 

```java
@Repository
public class DummyDao {
  @Autowired
  private JdbcTemplate template;
  public Dummy findDummyById(int id) {
    return template
        .queryForObject("SELECT * FROM dummy_test where id=?", new Object[]{id},
            new DummyRowMapper());
  }
 
  public List readAll() {
    return template.query("SELECT id,name_value FROM dummy_test", new DummyRowMapper());
  }
 
  public int save(Dummy dummy) {
    return template.update("INSERT INTO dummy_test (id,name_value) VALUES (?,?) ",
        new Object[]{dummy.getId(), dummy.getName()});
  }
 
  static class DummyRowMapper implements RowMapper {
    @Override
    public Dummy mapRow(ResultSet rs, int rowNumber) throws SQLException {
      return new Dummy(rs.getInt("id"), rs.getString("name_value"));
    }
  }
}
```

Look at the test bellow, it injects a DummyDAO instance and the following methods execute a read and write integration tests. Because this is an integration test, we are not_ _[_mocking_](https://pt.wikipedia.org/wiki/Objeto_Mock)_ _the behavior of the _DummyDAO_ class, this means that a real connection with the database is required. 

A static instance of the PostgresSQL container is created in the class beginning. Even though this instance is not used in the class body, it is responsible to create and start the container. 

The static initialization of the attribute allows us to reuse the same container instance in other running tests. This avoids that a new container is created and destroyed on an every executed test. Tha _@TestContainer_ and _@Container_ annotation takes care of the container life cycle and allows that it be started once and destroyed when the test ends. 

```java
@SpringBootTest
@ContextConfiguration
@Testcontainers
public class DummyDaoTest {
  
@Container
public static PostgreSQLContainer postgreSQLContainer = CustomPostgresContainer.getInstance();
  
@Autowired
DummyDao dao;
  
@Test
void injectedComponentsAreNotNull() {
  assertNotNull(dao);
}
 
@Test
void save_new_value() {
  Dummy value = new Dummy(1000, "dummy");
  int affectedRows = dao.save(value);
  assertEquals(1, affectedRows);
}
 
@Test
void read_all() {
  assertThat(dao.readAll()).isNotEmpty();
}
 
}
```

The _CustomPostgresContainer _class is responsible for download the image, create and start the _PostgreSQL_ container in the _alpine_ version. You could use any available image from [Docker Hub](https://hub.docker.com/_/postgres), or even a customized image. 

In this demonstration example, the container is started and a SQL Script is loaded to set up the database with some data. 

The overridden method _start()_ plays an important role in this test infrastructure. It gets the URL, user, and password from the started container and writes these values on environment variables. This allows the test to connect with a real database instance. 

```java
public class CustomPostgresContainer extends PostgreSQLContainer {
 
  private static final Logger logger = LoggerFactory.getLogger(CustomPostgresContainer.class);
  private static final String IMAGE_VERSION = "postgres:alpine";
  private static CustomPostgresContainer container;
 
  private CustomPostgresContainer() {
    super(IMAGE_VERSION);
  }
 
  public static CustomPostgresContainer getInstance() {
    if (container == null) {
      container = new CustomPostgresContainer();
    }
    return container;
  }
 
  @Override
  public void start() {
    super.start();
    logger.debug("POSTGRES INFO");
    logger.debug("DB_URL: " + container.getJdbcUrl());
    logger.debug("DB_USERNAME: " + container.getUsername());
    logger.debug("DB_PASSWORD: " + container.getPassword());
    System.setProperty("DB_URL", container.getJdbcUrl());
    System.setProperty("DB_USERNAME", container.getUsername());
    System.setProperty("DB_PASSWORD", container.getPassword());
  }
 
  @Override
  public void stop() {
    //do nothing, JVM handles shut down
  }
}
```

The summary of the test execution flow using the Testcontainer is the following: 

- The first test from the tests tacks is started;
- The _getInstance()_ method from _CustomPostgresContainer_ class is invoked. 
- The _Testcontainer_ library checks if the required image exists locally;
- If the image does not exist locally, it is downloaded from the _docker hub_;
- A container is created, based on the downloaded image and the database is started;
- All the following tests use the started database;
- At the end of all test execution, the container is destroyed. 

### Conclusion

In this article, we saw how the Testcontainer library makes easy the creation of test integration with the use of docker images. It allows us to decrease, the environment disparity between the development environment and production environment, it also brings safety to deploy. The library is evolving daily and brings native support to many other databases and allows you to create your image.  [In this video](https://www.youtube.com/watch?v=L_i61qTg510), [Kevin Wittek](https://github.com/kiview) one of the Testcontainer committers show us more about the library. 

Have you created integration tests that use real instances of services? What tools did you use? Drop a comment below! 

The post [Integration test with TestContainer](https://educosta.dev/blog/integration-test-with-testcontainer/) appeared first on [Be Cloud Native](https://educosta.dev).