Testing in Containers was never that easy 🙌🏼
Been working a lot with Docker lately in my last project and since we're doing Extreme Programming we go the TDD way. At some point I needed to test my micro services, specially one of the that was connecting to PostgreSQL. At first I started out using an embedded H2 DB but soon realised that as my DB schema evolved I couldn't keep up with H2 anymore. Plus, I wanted to be sure that the code that works in dev, works in the same way as in prod.
That's when I came across with this fantastic little tool called Testcontainers. It is made for the JVM, and you just need to have Docker installed on your machine. Supports JUnit 5, which is great, but also if you're an old timer, JUnit 4 is there!
Add it to your pom.xml:
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>postgresql</artifactId>
<version>${testcontainer.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId>
<version>${testcontainer.version}</version>
<scope>test</scope>
</dependency>
Next, simply create a class like this one:
@Testcontainers
public class PostgreSqlIntegrationTest {
static final PostgreSQLContainer postgreSqlContainer;
static {
postgreSqlContainer =
(PostgreSQLContainer)
new PostgreSQLContainer()
.withDatabaseName(PostgreSqlConfiguration.DB_NAME)
.withUsername(PostgreSqlConfiguration.USERNAME)
.withPassword(PostgreSqlConfiguration.PASSWORD)
.withReuse(true);
postgreSqlContainer.start();
}
This will be your base class, for all of your IntegrationTests. Since the container is a static final variable, the Testcontainers lib will realize that and keep the container alive across all of your test classes. That is a great advantage, since it takes a while to start up everything and if running on Jenkins it actually needs to download a fresh Image of Postgres. Finally a test class would look like:
@SpringBootTest
public class SomeServiceIntegrationTest extends PostgreSqlIntegrationTest{
@Autowired
private SomeService someService;
@Test
void testItOut(){
assertThat(someService.works()).isTrue();
}
}
Happy coding everyone! ☕️
Thanks for sharing! Usually I was using containers for microservice testing with NodeJS and I did not have this pain. I saved your article for the future - we never know what may happen tomorrow :)
Good one! Thank you for using JUnit5 =] I was playing around with the same stack (Spring + TestContainers + PostgreSQL) and something that made me want to bash my head against the wall was solved with @AutoConfigureTestDatabase(replace = NONE) on the test class. I was trying to verify the repository layer and without this annotation JUnit was replacing the application database config (so my container was up but the repo was pointing somewhere else). Fun times.