javaquarkusliquibasetestcontainers

Quarkus tests using test containers and Liquibase


I'm trying to write integration tests for our application and I've hit a roadblock I can't seem to overcome.

I've set my test resource

@Slf4j
public class PostgreSQLTestResource implements QuarkusTestResourceLifecycleManager {

  private static PostgreSQLContainer<?> postgreSQLContainer;

  @Override
  public Map<String, String> start() {
    log.info("Starting PostgreSQL with profile: {}", System.getProperty("quarkus.profile"));
    postgreSQLContainer =
        new PostgreSQLContainer<>("postgres:alpine")
            .withDatabaseName("test")
            .withUsername("postgres")
            .withPassword("password")
            .withReuse(true);
    postgreSQLContainer.start();

    Map<String, String> config = new HashMap<>();
    config.put("quarkus.datasource.jdbc.url", postgreSQLContainer.getJdbcUrl());
    config.put("quarkus.datasource.username", postgreSQLContainer.getUsername());
    config.put("quarkus.datasource.password", postgreSQLContainer.getPassword());
    config.put("quarkus.liquibase.jdbc.url", postgreSQLContainer.getJdbcUrl());

    log.info("JDBC URL: {}", postgreSQLContainer.getJdbcUrl());
    return config;
  }

  @Override
  public void stop() {
    if (postgreSQLContainer != null) {
      postgreSQLContainer.stop();
    }
  }
}

The test itself:

@QuarkusTest
@TestProfile(TestProfileResolver.class)
@QuarkusTestResource(value = PostgreSQLTestResource.class,restrictToAnnotatedClass = true)
public class XServiceTest {

  @Inject
  XTypeVersionService xTypeVersionService;

  @BeforeEach
  void setUp() {
    closeable = MockitoAnnotations.openMocks(this);
  }

  @AfterEach
  void closeMocks() throws Exception {
    closeable.close();
  }
...
    @Test
    void getXTypeVersion() {
      List<XTypeVersionEntity> guh = xTypeVersionService.get();      
      assertTrue(!guh.isEmpty());      
    }
}



And 

public class TestProfileResolver implements QuarkusTestProfile {

  @Override
  public String getConfigProfile() {
    String activeProfile = System.getProperty("quarkus.profile");
    log.info("Active profile: {}", activeProfile);
    return "test";
  }

  @Override
  public Map<String, String> getConfigOverrides() {
    String configLocation = System.getProperty("quarkus.config.locations");
    log.info("Config location: {}", configLocation);
    if (configLocation == null || configLocation.isEmpty()) {
      log.info("Setting config location to src/test/resources/application.yaml");
      return Map.of("quarkus.config.locations", "src/test/resources/application.yaml");
    }
    return Collections.emptyMap();
  }
}

And the test profile properties:

quarkus:
  profile: test
  http:
    test-port: 9300

  datasource:
    db-kind: postgresql
    jdbc:
      url: jdbc:tc:postgresql:alpine://test?TC_INITSCRIPT=init.sql
      driver: org.testcontainers.jdbc.ContainerDatabaseDriver

  hibernate-orm:
    database:
      generation: drop-and-create

  liquibase:
    enabled: true
    migrate-at-start: true
    change-log: classpath:db/dbChangelog-test.yaml
    default-schema-name: x
    validate-on-migrate: true
    jdbc-url: jdbc:tc:postgresql:alpine://test?TC_INITSCRIPT=init.sql

  log:
    min-level: DEBUG
    category:
      "io.quarkus":
        level: INFO
      "org.testcontainers":
        level: INFO
      "liquibase":
        level: TRACE

The problem is that it looks like the Liquibase scripts are being executed but the tests don't work. Meaning the test to get all x types fails because it can't find any. Digging deeper I notice that when I execute the test, 2 Postgres containers are created and shown in the docker desktop app. It is also corroborated in the console output where I can see 2 jdbcs that look similar but have different ports. I believe that the Liquibase script is being executed in one container and the application is accessing the second one.

Has anyone had an issue like this or better yet a working example of test containers with Liquibase (and better yet, using Quarkus)?


Solution

  • The reason why there are 2 postgres instances spinned up is, because Quarkus also does that for you automatically when you’re running in the test and dev profiles.

    You don’t need to explicitly create a QuarkusTestResourceLifecycleManager. As a matter of fact, you don’t need to specify your db url and do anything with Testcontainers.

    Just write your test and a db is automatically spinned up for you. You don’t have to configure anything, this is all handled.

    So you can remove all your code, except your test class and then it should look something like this:

    quarkus:
      http:
        test-port: 9300
    
      datasource:
        db-kind: postgresql
    
      hibernate-orm:
        database:
          generation: none <--> drop-and-create is going to conflict with liquibase
    
      liquibase:
        enabled: true
        migrate-at-start: true
        change-log: classpath:db/dbChangelog-test.yaml
        default-schema-name: x
        validate-on-migrate: true
    
    
    // quarkus will spin up postgres automatically from here
    @QuarkusTest
    @TestHTTPEndpoint(YourRestController.class) ---> this will automatically add the rootpath to RestAssured
    class MyTest {
    
       ...
    }
    

    For more info check these links: