Test af Spring Data + Spring Boot-applikationer med Arquillian (del 2)
I forrige indlæg skrev jeg om, hvordan man tester Forårsdata applikation ved hjælp af Docker med Arquillian Cube . Testen så sådan ud:
@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) ); } } }
Denne test starter lige Redis container, og udfyld derefter data ved hjælp af restTemplate og indlæg metode, og udfør derefter logikken under test (test af GET HTTP metode) og til sidst stoppe Redis container.
Det er godt, det virker, men der er flere problemer der:<
- Den første er, at vi bruger REST API at forberede datasæt af testen. Problemet her er, at testen muligvis mislykkes, ikke på grund af en fejl på kode under test, men på grund af forberedelsen af testen (indsættelse af data).
- Den anden er, at hvis POST endepunkt ændrer format/placering, så skal du huske at ændre alle steder i testene, hvor det bruges.
- Den sidste er, at hver test skal forlade miljøet som fundet før udførelse, så testen er isoleret fra alle udførelser. Problemet er, at for at gøre det i denne tilgang skal du slette de tidligere elementer indsat af POST . Det betyder at tilføje SLET HTTP metode, som måske ikke altid er implementeret i slutpunktet, eller den kan være begrænset til nogle konkrete brugere, så de er nødt til at håndtere særlige godkendelsesting.
For at undgå dette problem Arquillian Persistence Extension (alias APE ) var lavet. Disse udvidelser integreres med DBUnit og Flyway til SQL databaser, NoSQLUnit for Ingen SQL databaser og Postmand-samlinger til REST tjenester, så du kan udfylde din backend, før du tester den rigtige testbrug, og rense persistenslageret efter testen er udført.
Også befolkningsdata er gemt inde i en fil, så det betyder, at de kan genbruges i alle test og nemt ændres i tilfælde af enhver skemaopdatering.
Lad os se eksempel på del 1 af indlægget, men opdatering til brug af 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) ); } } }
Og filen (pings.json ) bruges til at udfylde Redis instans med data ser sådan ud:
{ "data" : [ { "list" : [ { "key" : "ping", "values" : [ { "value" : "pong" }, { "value" : "pung" } ] } ] } ] }Bemærk, at du i denne test har erstattet POST kalder på noget, der direkte sættes ind i lageret. På denne måde undgår du enhver fejl, der måtte opstå i indsættelseslogikken (som ikke er den del, der testes). Til sidst efter hver testmetode, Redis instans er renset, så andre test finder Redis rent og i kendt tilstand.
Projektet kan findes på https://github.com/arquillian-testing-microservices/pingpongbootredis