loading...

Micronaut integration testing using testcontainers

major13ua profile image Ievgen ・2 min read

Micronaut is a modern, JVM-based, full-stack framework for building modular, easily testable microservice and serverless applications. Concepts, API, annotations, and conventions are similar to Spring Framework, but some features should be done differently or missing.

One of these missing is ApplicationContextInitializer which is really handy for application context configuration in runtime, before component initialization. This feature is used to get Testcontainers instance host and port, as Testcontainers uses randomized ports for each container it starts, but makes it easy to obtain the actual port at runtime.

Spring Boot configuration and ApplicationContextInitializer described with great details in the article, and here I will try to fill the gap for Micronaut.

Option1. Lazy and deprecated.

withCreateContainerCmdModifier could be used to set known port instead of random.

public abstract class ResourceProvider {

    private static final int REDIS_PORT = 6379;

    @Container
    static GenericContainer redis = new GenericContainer<>("redis:5.0.8-alpine")
            .withCreateContainerCmdModifier(it -> it.withName("redis-unit")
                    .withPortBindings(new PortBinding(Ports.Binding.bindPort(REDIS_PORT), new ExposedPort(REDIS_PORT))))
            .withExposedPorts(REDIS_PORT);

}
Enter fullscreen mode Exit fullscreen mode

but, withPortBindings deprecated, and will be removed in future releases. Main idea behind this, is virtualization and scoping. Your application tests should be independent, so they could be executed in parallel, using same docker. From the other hand same Docker/Jenkins environment frequently used for multiple projects, and in this case you will receive "Bind for 0.0.0.0:6379 failed: port is already allocated" when both tests will require same container.

Option2. Micronaut way solution.

Micronaut offers PropertySource concept to initiate application configuration with host/port values obtained from testcontainer.
Example for EmbeddedServer

@BeforeAll
public static void initUnitTest() {

    embeddedServer = ApplicationContext.run(EmbeddedServer.class, PropertySource.of(
            "test", Map.of("redis.host", redis.getContainerIpAddress(), "redis.port", redis.getMappedPort(REDIS_PORT))
    ));

    context = embeddedServer.getApplicationContext();

}
Enter fullscreen mode Exit fullscreen mode

Example for ApplicationContext

@BeforeAll
public static void initUnitTest() {
    context = ApplicationContext.run(PropertySource.of(
            "test", Map.of("redis.host", redis.getContainerIpAddress(), "redis.port", redis.getMappedPort(REDIS_PORT))
    ));
}
Enter fullscreen mode Exit fullscreen mode

End to end demo could be found here.
Happy codding and testing!

Discussion

pic
Editor guide