在上一篇文章中 ,我写了关于如何使用Arquillian Cube和 Docker一起测试Spring Data应用程序的信息。 测试看起来像:
@RunWith(SpringRunner.class)
@SpringBootTest(classes = PingPongController.class, webEnvironment = RANDOM_PORT)
@ContextConfiguration(initializers = PingPongSpringBootTest.Initializer.class)
public class PingPongSpringBootTest {
@ClassRule
public static ContainerDslRule redis = new ContainerDslRule("redis:3.2.6")
.withPortBinding(6379);
@Autowired
TestRestTemplate restTemplate;
@Test
public void should_get_pongs() {
// given
restTemplate.postForObject("/ping", "pong", String.class);
restTemplate.postForObject("/ping", "pung", String.class);
// when
final List<String> pings = restTemplate.getForObject("/ping", List.class);
// then
assertThat(pings)
.hasSize(2)
.containsExactlyInAnyOrder("pong", "pung");
}
public static class Initializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
EnvironmentTestUtils.addEnvironment("testcontainers", configurableApplicationContext.getEnvironment(),
"spring.redis.host=" + redis.getIpAddress(),
"spring.redis.port=" + redis.getBindPort(6379)
);
}
}
}
该测试只是启动Redis容器,然后使用restTemplate和post方法填充数据,然后执行被测逻辑(测试GET HTTP方法),最后停止Redis容器。
很好,它可以工作,但是那里有几个问题:<
- 第一个是我们正在使用REST API准备测试的数据集。 这里的问题是,测试失败的原因可能不是因为被测代码失败,而是因为测试的准备工作(数据插入)。
- 第二个问题是,如果POST端点更改了格式/位置,那么您需要记住在使用它的测试中的任何地方进行更改。
- 最后一个是每个测试都应离开执行前的环境,因此该测试与所有执行隔离。 问题在于,要以这种方式执行此操作,您需要删除POST插入的先前元素。 这意味着添加DELETE HTTP方法,该方法可能并不总是在端点中实现,或者可能仅限于某些具体用户,因此需要处理特殊的身份验证事项。
为了避免此问题, 创建了Arquillian Persistence Extension (aka APE )。 这与DbUnit和迁飞扩展集成了SQL数据库,为NoSQLUnit 没有SQL数据库和邮差集合了REST服务,让您可以在测试,真正的考验的情况下使用前填充您的后端和执行测试后清理持久性存储。
此外,填充数据也存储在文件内,因此这意味着可以在所有测试中重复使用,并且在进行任何模式更新时可以轻松进行更改。
让我们看一下文章第1部分的示例,但将其更新为使用APE 。
@RunWith(SpringRunner.class)
@SpringBootTest(classes = PingPongController.class, webEnvironment = RANDOM_PORT)
@ContextConfiguration(initializers = PingPongSpringBootTest.Initializer.class)
public class PingPongSpringBootTest {
@ClassRule
public static ContainerDslRule redis = new ContainerDslRule("redis:3.2.6")
.withPortBinding(6379);
@Rule
public ArquillianPersistenceRule arquillianPersistenceRule = new ArquillianPersistenceRule();
@Autowired
TestRestTemplate restTemplate;
@Redis
@ArquillianResource
NoSqlPopulator populator;
@Test
public void should_get_pongs() {
// given
populator.forServer(redis.getIpAddress(), redis.getBindPort(6379))
.usingDataSet("pings.json")
.execute();
// when
final List<String> pings = restTemplate.getForObject("/ping", List.class);
// then
assertThat(pings)
.hasSize(2)
.containsExactlyInAnyOrder("pong", "pung");
}
@After
public void clean_database() {
populator.forServer(redis.getIpAddress(), redis.getBindPort(6379))
.clean();
}
public static class Initializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
EnvironmentTestUtils.addEnvironment("testcontainers", configurableApplicationContext.getEnvironment(),
"spring.redis.host=" + redis.getIpAddress(),
"spring.redis.port=" + redis.getBindPort(6379)
);
}
}
}
用于填充Redis实例的文件( pings.json )如下所示:
{
"data" : [
{
"list" : [
{
"key" : "ping",
"values" : [
{
"value" : "pong"
},
{
"value" : "pung"
}
]
}
]
}
]
}
请注意,在此测试中,您已将POST调用替换为直接插入到存储中的内容。
这样,您可以避免在插入逻辑(不是被测部件)中可能发生的任何故障。
最终,在每种测试方法之后,都会清理Redis实例,以便其他测试将Redis清理到已知状态。
该项目可以在https://github.com/arquillian-testing-microservices/pingpongbootredis中找到
该项目可以在https://github.com/arquillian-testing-microservices/pingpongbootredis中找到