Connection Timeout with testcontainers and redis

2.8k Views Asked by At

I do integration tests using Spring Boot, TestContainers, redis and Junit 5. I am facing a weird behavior, when I all the integration tests, I keep having this log displaying :

Cannot reconnect to [localhost:55133]: Connection refused: localhost/127.0.0.1:55133

and this exception :

org.springframework.dao.QueryTimeoutException: Redis command timed out; nested exception is io.lettuce.core.RedisCommandTimeoutException: Command timed out after 1 minute(s)

at org.springframework.data.redis.connection.lettuce.LettuceExceptionConverter.convert(LettuceExceptionConverter.java:70)

But I run the tests individually, I dont have this behavior.

I use Junit5 and I am using Junit5 extension to start and stop my redis container :

public class RedisTestContainerExtension implements BeforeAllCallback, AfterAllCallback {

    private GenericContainer<?> redis;

    @Override
    public void beforeAll(ExtensionContext extensionContext) throws Exception {
        redis = new GenericContainer<>(DockerImageName.parse("redis:5.0.3-alpine"))
                .withCommand("redis-server","--requirepass", "password")
                .waitingFor(Wait.forListeningPort())
                .withStartupTimeout(Duration.ofMinutes(2))
                .withExposedPorts(6379);
        redis.start();
        System.setProperty("APP_REDIS_CONVERSATIONS_HOST",redis.getHost());
        System.setProperty("APP_REDIS_CONVERSATIONS_PORT",redis.getFirstMappedPort().toString());
        System.setProperty("APP_REDIS_CONVERSATIONS_PASSWORD","password");
        System.setProperty("APP_REDIS_CONVERSATIONS_TTL","600m");
    }

    @Override
    public void afterAll(ExtensionContext extensionContext) throws Exception {
        if(redis != null){
            redis.stop();
        }
    }
}

And I add this file as an extension to my integration test :

@ExtendWith({SpringExtension.class, RedisTestContainerExtension.class})
@SpringBootTest(classes = ConversationsApplication.class)
class MyIntegrationTest {
 ...
 }

Can anyone help me fix this situation.

1

There are 1 best solutions below

0
On

We had a similar issue. The issue was occured only when we execute all tests (or at least not only one specific)

We have another test setup - we are using a base class to manage test testcontainers - where the port-mapping of the containers was applied by overriding the properties via DynamicPropertySource

Our fix was to mark the base-test-class with @DirtiesContext that spring does not reuse the application-context over the tests-classes - see documentation of DynamicPropertySource:

NOTE: if you use @DynamicPropertySource in a base class and discover that tests in subclasses fail because the dynamic properties change between subclasses, you may need to annotate your base class with @DirtiesContext to ensure that each subclass gets its own ApplicationContext with the correct dynamic properties.

Example:

@Slf4j
@SpringBootTest
@DirtiesContext
@Testcontainers
public abstract class AbstractContainerTest {

  @Container
  private static final ElasticsearchContainer elasticsearchContainer = new DealElasticsearchContainer();

  @Container
  private static final RedisCacheContainer redisCacheContainer = new RedisCacheContainer();

  @DynamicPropertySource
  static void databaseProperties(DynamicPropertyRegistry registry) {
    log.info("Override properties to connect to Testcontainers:");
    log.info("* Test-Container 'Elastic': spring.elasticsearch.rest.uris = {}",
        elasticsearchContainer.getHttpHostAddress());
    log.info("* Test-Container 'Redis': spring.redis.host = {} ; spring.redis.port = {}",
        redisCacheContainer.getHost(), redisCacheContainer.getMappedPort(6379));

    registry.add("spring.elasticsearch.rest.uris", elasticsearchContainer::getHttpHostAddress);
    registry.add("spring.redis.host", redisCacheContainer::getHost);
    registry.add("spring.redis.port", () -> redisCacheContainer.getMappedPort(6379));
  }

}

So maybe give it a try to use @DirtiesContext or switch to a setup which uses DynamicPropertySource to override the properties. It was especially build for this case:

Method-level annotation for integration tests that need to add properties with dynamic values to the Environment's set of PropertySources.

This annotation and its supporting infrastructure were originally designed to allow properties from Testcontainers based tests to be exposed easily to Spring integration tests. However, this feature may also be used with any form of external resource whose lifecycle is maintained outside the test's ApplicationContext.