Testcontainer 的 Redis 容器连接到与测试中定义的容器不同的容器
Testcontainer's Redis container connects to a different container then the one defined in the test
我正在 Spring 启动应用程序中进行集成测试。该应用程序需要使用 Redis。
在开发阶段,我有一个应用程序连接到的本地 Redis 容器。
对于集成测试,我使用 testcontainers and I also followed their example of how to use a Redis container。
在某些时候,我了解到测试 运行 只有在开发容器启动并且 运行ning 时才能正确。如果关闭,则集成测试正在下降,因为它们无法访问 Redis。
所以集成测试 class 看起来像这样:
@RunWith(SpringRunner.class)
@SpringBootTest(classes = SharkApplication.class,
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@TestPropertySource(locations = "classpath:application-integrationtests.yml")
@AutoConfigureMockMvc
public class SharkIntegrationTest {
static GenericContainer redis = new GenericContainer("redis:3.0.6")
.withExposedPorts(6379);
@BeforeClass
public static void before(){
redis.start();
}
@AfterClass
public static void after(){
redis.stop();
}
...
当运行测试时,我可以在日志中看到:
14:36:24.372 [main] DEBUG [redis:3.0.6] - Starting container: redis:3.0.6
14:36:24.372 [main] DEBUG [redis:3.0.6] - Trying to start container:
redis:3.0.6
14:36:24.373 [main] DEBUG [redis:3.0.6] - Trying to start container:
redis:3.0.6 (attempt 1/1)
14:36:24.373 [main] DEBUG [redis:3.0.6] - Starting container: redis:3.0.6
14:36:24.373 [main] INFO [redis:3.0.6] - Creating container for image:
redis:3.0.6
...
14:36:25.282 [main] INFO [redis:3.0.6] - Container redis:3.0.6 started
但是由于无法访问 Redis,应用程序失败了:
Caused by: redis.clients.jedis.exceptions.JedisConnectionException: java.net.ConnectException: Connection refused: connect
在某些时候,我试图更改容器应该启动的端口。从 6379 到 16379(在代码和 yml 文件中都发生了变化),但随后测试进入无限循环并打印到屏幕:
14:41:57.258 [ducttape-0] DEBUG org.testcontainers.containers.ExecInContainerPattern - /amazing_beaver: Running "exec" command: /bin/bash -c </dev/tcp/localhost/16379 && echo
您遗漏了测试容器的一个非常重要的方面——随机端口。
来自您提到的link:
For example, with the Redis example above, the following will allow your tests to access the Redis service:
String redisUrl = redis.getContainerIpAddress() + ":" + redis.getMappedPort(6379);
测试容器以随机端口启动一切以避免冲突。
您可以按照this workshop正确集成。
当您以这种方式声明容器时:
static GenericContainer redis = new GenericContainer("redis:3.0.6")
.withExposedPorts(6379);
您要告诉 TestContainers 将随机主机端口映射到容器端口 6379
。如下截图所示,例如TestContainers从主机端口32881
映射到容器端口6379
:
测试访问Redis容器需要使用随机主机端口,不能使用redis端口6379
。为此,您需要覆盖 (在运行时) application.properties
中定义的配置值以使用随机主机端口。
以下是您的操作方法:
package some.random.packagee;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.support.TestPropertySourceUtils;
import org.testcontainers.containers.GenericContainer;
@SpringBootTest
@ContextConfiguration(initializers = some.random.packagee.AbstractContainerBaseTest.Initializer.class)
public class AbstractContainerBaseTest {
private static final int REDIS_PORT = 6379;
// Optional
@Autowired
private RedisTemplate redisTemplate;
// Optional
protected void cleanCache() {
redisTemplate.getConnectionFactory().getConnection().flushAll();
}
public static class Initializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
static GenericContainer redis = new GenericContainer<>("redis:6-alpine")
.withExposedPorts(REDIS_PORT)
.withReuse(true);
@Override
public void initialize(ConfigurableApplicationContext context) {
// Start container
redis.start();
// Override Redis configuration
String redisContainerIP = "spring.redis.host=" + redis.getContainerIpAddress();
String redisContainerPort = "spring.redis.port=" + redis.getMappedPort(REDIS_PORT); // <- This is how you get the random port.
TestPropertySourceUtils.addInlinedPropertiesToEnvironment(context, redisContainerIP, redisContainerPort); // <- This is how you override the configuration in runtime.
}
}
}
然后在需要使用Redis的class中扩展class AbstractContainerBaseTest
,例如:
package some.random.packagee;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
class CacheTest extends AbstractContainerBaseTest {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@AfterEach
void tearDown() {
cleanCache();
}
@Test
public void testSomeMethodUsingRedis() {
// Add your test here.
}
}
我正在 Spring 启动应用程序中进行集成测试。该应用程序需要使用 Redis。
在开发阶段,我有一个应用程序连接到的本地 Redis 容器。
对于集成测试,我使用 testcontainers and I also followed their example of how to use a Redis container。
在某些时候,我了解到测试 运行 只有在开发容器启动并且 运行ning 时才能正确。如果关闭,则集成测试正在下降,因为它们无法访问 Redis。
所以集成测试 class 看起来像这样:
@RunWith(SpringRunner.class)
@SpringBootTest(classes = SharkApplication.class,
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@TestPropertySource(locations = "classpath:application-integrationtests.yml")
@AutoConfigureMockMvc
public class SharkIntegrationTest {
static GenericContainer redis = new GenericContainer("redis:3.0.6")
.withExposedPorts(6379);
@BeforeClass
public static void before(){
redis.start();
}
@AfterClass
public static void after(){
redis.stop();
}
...
当运行测试时,我可以在日志中看到:
14:36:24.372 [main] DEBUG [redis:3.0.6] - Starting container: redis:3.0.6
14:36:24.372 [main] DEBUG [redis:3.0.6] - Trying to start container:
redis:3.0.6
14:36:24.373 [main] DEBUG [redis:3.0.6] - Trying to start container:
redis:3.0.6 (attempt 1/1)
14:36:24.373 [main] DEBUG [redis:3.0.6] - Starting container: redis:3.0.6
14:36:24.373 [main] INFO [redis:3.0.6] - Creating container for image:
redis:3.0.6
...
14:36:25.282 [main] INFO [redis:3.0.6] - Container redis:3.0.6 started
但是由于无法访问 Redis,应用程序失败了:
Caused by: redis.clients.jedis.exceptions.JedisConnectionException: java.net.ConnectException: Connection refused: connect
在某些时候,我试图更改容器应该启动的端口。从 6379 到 16379(在代码和 yml 文件中都发生了变化),但随后测试进入无限循环并打印到屏幕:
14:41:57.258 [ducttape-0] DEBUG org.testcontainers.containers.ExecInContainerPattern - /amazing_beaver: Running "exec" command: /bin/bash -c </dev/tcp/localhost/16379 && echo
您遗漏了测试容器的一个非常重要的方面——随机端口。
来自您提到的link:
For example, with the Redis example above, the following will allow your tests to access the Redis service:
String redisUrl = redis.getContainerIpAddress() + ":" + redis.getMappedPort(6379);
测试容器以随机端口启动一切以避免冲突。
您可以按照this workshop正确集成。
当您以这种方式声明容器时:
static GenericContainer redis = new GenericContainer("redis:3.0.6")
.withExposedPorts(6379);
您要告诉 TestContainers 将随机主机端口映射到容器端口 6379
。如下截图所示,例如TestContainers从主机端口32881
映射到容器端口6379
:
测试访问Redis容器需要使用随机主机端口,不能使用redis端口6379
。为此,您需要覆盖 (在运行时) application.properties
中定义的配置值以使用随机主机端口。
以下是您的操作方法:
package some.random.packagee;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.support.TestPropertySourceUtils;
import org.testcontainers.containers.GenericContainer;
@SpringBootTest
@ContextConfiguration(initializers = some.random.packagee.AbstractContainerBaseTest.Initializer.class)
public class AbstractContainerBaseTest {
private static final int REDIS_PORT = 6379;
// Optional
@Autowired
private RedisTemplate redisTemplate;
// Optional
protected void cleanCache() {
redisTemplate.getConnectionFactory().getConnection().flushAll();
}
public static class Initializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
static GenericContainer redis = new GenericContainer<>("redis:6-alpine")
.withExposedPorts(REDIS_PORT)
.withReuse(true);
@Override
public void initialize(ConfigurableApplicationContext context) {
// Start container
redis.start();
// Override Redis configuration
String redisContainerIP = "spring.redis.host=" + redis.getContainerIpAddress();
String redisContainerPort = "spring.redis.port=" + redis.getMappedPort(REDIS_PORT); // <- This is how you get the random port.
TestPropertySourceUtils.addInlinedPropertiesToEnvironment(context, redisContainerIP, redisContainerPort); // <- This is how you override the configuration in runtime.
}
}
}
然后在需要使用Redis的class中扩展class AbstractContainerBaseTest
,例如:
package some.random.packagee;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
class CacheTest extends AbstractContainerBaseTest {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@AfterEach
void tearDown() {
cleanCache();
}
@Test
public void testSomeMethodUsingRedis() {
// Add your test here.
}
}