Reputation: 825
I'm trying to add automated testing using the TestContainers library to my Spring Boot project
Here is my test class to test my jpa repository:
package com.ubm.mfi.repo;
import com.ubm.mfi.domain.MasterFileIndexRow;
import org.junit.ClassRule;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.util.TestPropertyValues;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.testcontainers.containers.PostgreSQLContainer;
import org.testcontainers.junit.jupiter.Testcontainers;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
@ExtendWith(SpringExtension.class)
@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@Testcontainers
@ContextConfiguration(initializers = { MasterFileIndexRowRepoTest.Initializer.class })
public class MasterFileIndexRowRepoTest {
@ClassRule
public static PostgreSQLContainer<?> postgreSQLContainer = new PostgreSQLContainer<>("postgres:latest");
@Autowired
private MasterFileIndexRowRepo masterFileIndexRowRepo;
// write test cases here
@Test
public void whenFindAllRows_thenSizeIsGreaterThanZero() {
// when
List<MasterFileIndexRow> rows = masterFileIndexRowRepo.findAll();
// then
assertThat(rows.size())
.isGreaterThan(0);
}
static class Initializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
TestPropertyValues
.of("spring.datasource.url=" + postgreSQLContainer.getJdbcUrl(),
"spring.datasource.username=" + postgreSQLContainer.getUsername(),
"spring.datasource.password=" + postgreSQLContainer.getPassword())
.applyTo(configurableApplicationContext.getEnvironment());
}
}
}
Here are the dependencies in my build.gradle
testCompile "org.testcontainers:testcontainers:1.14.1"
testCompile "org.testcontainers:postgresql:1.14.1"
When running the Test I get this error: Caused by: java.lang.IllegalStateException: Mapped port can only be obtained after the container is started
From what I've seen, the container should start when starting the test, does anybody know what I'm missing?
Upvotes: 42
Views: 48563
Reputation: 5126
When you use @TestInstance(Lifecycle.PER_CLASS)
better declare your test containers in an interface and import in test class like
@Testcontainers
public interface Containers {
@Container @ServiceConnection
public static final PostgreSQLContainer<?> postgres =
new PostgreSQLContainer<>("postgres:latest");
}
@TestInstance(Lifecycle.PER_CLASS)
@ImportTestcontainers(Containers.class)
class TestClass {
// here you can use all @BeforeAll and other lifecycle methods
}
Do not start and stop containers - it's taken care by @TestContainers
Excerpt from the Javadoc of @TestContainers
@Testcontainers is a JUnit Jupiter extension to activate automatic startup and stop of containers used in a test case.
The Testcontainers extension finds all fields that are annotated with Container and calls their container lifecycle methods. Containers declared as static fields will be shared between test methods. They will be started only once before any test method is executed and stopped after the last test method has executed. Containers declared as instance fields will be started and stopped for every test method.
The annotation @Testcontainers can be used on a superclass in the test hierarchy as well. All subclasses will automatically inherit support for the extension.
Note: This extension has only been tested with sequential test execution. Using it with parallel test execution is unsupported and may have unintended side effects.
Upvotes: 3
Reputation:
The solution as mentioned here is to start the container after the declaration. I only had this issue when I changed the lifecycle of my tests to per class.
Upvotes: 0
Reputation: 31
This problem occured when I still used Junit 4 org.junit.Test to annotate test methods. Adding the explicit start command as suggested by Dzmitry worked for me in this case. As the more appropriate remediation, I annotated my test methods with Junit 5 org.junit.jupiter.api.Test annotation. In this case, the containers start without an explicit start command.
Below worked for me
import org.junit.jupiter.api.Test;
import org.testcontainers.containers.PostgreSQLContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
@Testcontainers
public class DataBaseConnectionTest {
@Container
private static final PostgreSQLContainer<?> databaseContainer = new PostgreSQLContainer<>("postgres:14");
...
@Test
void test1() {
System.out.println(databaseContainer..getJdbcUrl());
}
}
The following are my Maven dependencies:
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.9.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.9.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId>
<version>1.17.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers</artifactId>
<version>1.17.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>postgresql</artifactId>
<version>1.17.5</version>
<scope>test</scope>
</dependency>
Upvotes: 3
Reputation: 1301
Not sure if it does help to smdy but for me starting the actual test container after its definition solved the problem:
@Container
private static final PostgreSQLContainer DATABASE = new PostgreSQLContainer("postgres:14");
static {
DATABASE.start();
}
// any kind of DynamicPropertySources or initializers
// goes below and can successfully get mapped port from DATABASE.getJdbcUrl()
Upvotes: 19
Reputation: 81930
You are trying to use PostgresSQLContainer
as JUnit ClassRule
but your usage of @ExtendWith
seems to indicate that you are using JUnit 5 / Jupiter which does not support JUnit 4 rules.
Use the JUnit 5 integration of Testcontainers instead: https://www.testcontainers.org/test_framework_integration/junit_5/
Upvotes: 24