'How to connect to testcontainers redis correctly during spring boot integration test?

I am writing test for my service in spring boot

@Component
public class MyService {
    @Autowired
    StringRedisTemplate stringRedisTemplate;
    // a number of other @Autowired dependencies

    public User getUser(String uuid) {
        var key = String.format("user:%s", uuid);
        var cache = stringRedisTemplate.opsForValue().get(key);
        if (cache == null) {
            // return user from database
        } else {
            // return user from deserialized cache
        }
    }
}

@Testcontainers
@SpringBootTest
class MyServiceTest {
    @Autowired
    StringRedisTemplate stringRedisTemplate;
    @Autowired
    MyService myService;
    
    @Container
    public static GenericContainer<?> redis =
        new GenericContainer<>("redis:5.0.14-alpine3.15").withExposedPorts(6379);

    @BeforeClass
    public static void startContainer() {
        redis.start();
        var redisUrl = String.format("redis://%s:%s", redis.getHost(), redis.getMappedPort(6379));
        System.setProperty("spring.redis.url", redisUrl);
    }

    @AfterClass
    public static void stopContainer() {
        redis.stop();
    }

    @Test
    void getUser_returnCachedUser() {
        // breakpoint here
        stringRedisTemplate.opsForValue().set("user:some-uuid", "{\"uuid\":\"some-uuid\",\"name\":\"cache\"}");
        var user = myService.getUser("some-uuid");
        assertEquals("cache", user.name);
    }
}

When I ran this in debug mode and hit breakpoint, I noticed port redis.getMappedPort(6379) in console was not equal to stringRedisTemplate.connectionFactory.client or myService.stringRedisTemplate.connectionFactory.client.

Did System.setProperty overwrite properties and take effect in this case? How can I use testcontainers in spring boot integration test?



Solution 1:[1]

I'd suggest to use slightly different container from playtika which is built on top of testcontainers.

What you need to do is to include spring-cloud-starter-bootstrap in your pom.xml (it's enough as a test dependency).

and then in your test application.yaml|properties use following:

spring:
  redis:
    port: ${embedded.redis.port}
    password: ${embedded.redis.password}
    host: ${embedded.redis.host}
    ssl: false

Solution 2:[2]

You can use getFirstMappedPort() rather than redis.getMappedPort(6379) because testcontainer uses random ports. 6379 is host machine port, but the redis container's port is randomly assigned to avoid conflicts. More details could be found in another thread: https://stackoverflow.com/a/50869731

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 bilak
Solution 2 enesify