Test i Spring Boot

1. Oversigt

I denne vejledning skal vi se på skrive tests ved hjælp af rammesupport i Spring Boot. Vi dækker enhedstest, der kan køre isoleret, såvel som integrationstest, der starter boot-foråret sammenhæng, før test udføres.

Hvis du er ny på Spring Boot, så tjek vores introduktion til Spring Boot.

2. Opsætning af projekt

Den applikation, vi skal bruge i denne artikel, er en API, der giver nogle grundlæggende operationer på en Medarbejder Ressource. Dette er en typisk differentieret arkitektur - API-opkaldet behandles fra Controller til Service til Udholdenhed lag.

3. Maven-afhængigheder

Lad os først tilføje vores testafhængigheder:

 org.springframework.boot spring-boot-starter-test test 2.2.6.RELEASE com.h2database h2 test 

Det spring-boot-starter-test er den primære afhængighed, der indeholder de fleste elementer, der kræves til vores tests.

H2 DB er vores in-memory database. Det eliminerer behovet for at konfigurere og starte en faktisk database til testformål.

4. Integrationstestning med @DataJpaTest

Vi skal arbejde med en enhed, der hedder Medarbejder, som har en id og en navn som dets egenskaber:

@Entity @Table (name = "person") offentlig klasse Medarbejder {@Id @GeneratedValue (strategi = GenerationType.AUTO) privat Lang id; @Size (min = 3, max = 20) privat strengnavn; // standard getters og settere, konstruktører}

Og her er vores lager ved hjælp af Spring Data JPA:

@Repository offentlig grænseflade EmployeeRepository udvider JpaRepository {offentlig medarbejder findByName (strengnavn); }

Det er det for persistenslagskoden. Lad os nu gå mod at skrive vores testklasse.

Lad os først oprette skelettet i vores testklasse:

@RunWith (SpringRunner.class) @DataJpaTest offentlig klasse EmployeeRepositoryIntegrationTest {@Autowired private TestEntityManager entityManager; @Autowired privat EmployeeRepository medarbejderRepository; // skriv testcases her}

@RunWith (SpringRunner.class) giver en bro mellem Spring Boot testfunktioner og JUnit. Når vi bruger Spring Boot-testfunktioner i vores JUnit-tests, skal denne kommentar kræves.

@DataJpaTest giver nogle standardopsætninger, der er nødvendige for at teste persistenslaget:

  • konfiguration af H2, en in-memory database
  • indstilling af dvale, fjerdata og Datakilde
  • udfører en @EntityScan
  • aktivering af SQL-logning

For at udføre DB-operationer har vi brug for nogle poster, der allerede er i vores database. For at opsætte disse data kan vi bruge TestEntityManager.

Spring Boot TestEntityManager er et alternativ til standard JPA EntityManager der giver metoder, der ofte bruges til at skrive prøver.

Medarbejderopbevaring er den komponent, som vi skal teste.

Lad os nu skrive vores første testsag:

@Test offentlig ugyldig nårFindByName_thenReturnEmployee () {// givet medarbejder alex = ny medarbejder ("alex"); entityManager.persist (alex); entityManager.flush (); // når medarbejder fundet = medarbejderRepository.findByName (alex.getName ()); // derefter assertThat (found.getName ()) .isEqualTo (alex.getName ()); }

I ovenstående test bruger vi TestEntityManager at indsætte en Medarbejder i DB og læser det via find by name API.

Det hævder, at (…) del kommer fra Assertj-biblioteket, som leveres sammen med Spring Boot.

5. Mocking With @MockBean

Vores Service lagkode er afhængig af vores Datalager.

Men for at teste Service lag, behøver vi ikke vide eller bekymre os om, hvordan persistenslaget implementeres:

@Service offentlig klasse EmployeeServiceImpl implementerer EmployeeService {@Autowired private EmployeeRepository employeeRepository; @ Override offentlig medarbejder getEmployeeByName (strengnavn) {returner medarbejderRepository.findByName (navn); }}

Ideelt set skulle vi være i stand til at skrive og teste vores Service lagkode uden ledninger i vores fulde persistenslag.

For at opnå dette, Vi kan bruge den mocking support, der leveres af Spring Boot Test.

Lad os først se på testklasseskelettet:

@RunWith (SpringRunner.class) offentlig klasse EmployeeServiceImplIntegrationTest {@TestConfiguration statisk klasse EmployeeServiceImplTestContextConfiguration {@Bean offentlig EmployeeService medarbejderService () {returner ny EmployeeServiceImpl (); }} @Autowired private EmployeeService employeeService; @MockBean private EmployeeRepository medarbejderRepository; // skriv testcases her}

For at kontrollere Service klasse, skal vi have en forekomst af Service klasse oprettet og tilgængelig som en @Bønne så vi kan @Autowire det i vores testklasse. Vi kan opnå denne konfiguration ved hjælp af @TestConfiguration kommentar.

Under komponentscanning finder vi muligvis ud, at komponenter eller konfigurationer, der kun er oprettet til specifikke tests, ved et uheld bliver afhentet overalt. For at forhindre dette, Spring Boot leverer @TestConfiguration kommentar, som vi kan tilføje på klasser i src / test / java for at angive, at de ikke skal afhentes ved scanning.

En anden interessant ting her er brugen af @MockBean. Det skaber en Mock for Medarbejderopbevaring, som kan bruges til at omgå opkaldet til det aktuelle Medarbejderopbevaring:

@Før offentlig ugyldig setUp () {Medarbejder alex = ny medarbejder ("alex"); Mockito.when (medarbejderRepository.findByName (alex.getName ())) .thenReturn (alex); }

Da opsætningen er færdig, vil testtilfældet være enklere:

@Test offentlig ugyldig nårValidName_thenEmployeeShouldBeFound () {String name = "alex"; Medarbejder fundet = employeeService.getEmployeeByName (navn); assertThat (found.getName ()) .isEqualTo (name); }

6. Enhedstest med @WebMvcTest

Vores Controller afhænger af Service lag; lad os kun inkludere en enkelt metode for enkelhed:

@RestController @RequestMapping ("/ api") offentlig klasse EmployeeRestController {@Autowired privat EmployeeService medarbejderService; @GetMapping ("/ ansatte") offentlig liste getAllEmployees () {return medarbejderService.getAllEmployees (); }}

Da vi kun har fokus på Controller kode, det er naturligt at spotte Service lagkode til vores enhedstest:

@RunWith (SpringRunner.class) @WebMvcTest (EmployeeRestController.class) offentlig klasse EmployeeRestControllerIntegrationTest {@Autowired private MockMvc mvc; @MockBean privat EmployeeService-service; // skriv testcases her}

For at teste Controllere, vi kan bruge @WebMvcTest. Det konfigurerer automatisk Spring MVC-infrastrukturen til vores enhedstest.

I de fleste tilfælde, @WebMvcTest vil være begrænset til at starte en enkelt controller. Vi kan også bruge det sammen med @MockBean at levere mock-implementeringer til alle nødvendige afhængigheder.

@WebMvcTest også auto-konfigurationer MockMvc, som tilbyder en effektiv måde at let teste MVC-controllere uden at starte en fuld HTTP-server.

Når det er sagt, lad os skrive vores test case:

@Test offentligt ugyldigt givetEmployees_whenGetEmployees_thenReturnJsonArray () kaster undtagelse {Medarbejder alex = ny medarbejder ("alex"); Liste over alle medarbejdere = Arrays.asList (alex); given (service.getAllEmployees ()). willReturn (allEmployees); mvc.perform (get ("/ api / ansatte") .contentType (MediaType.APPLICATION_JSON)) .ogExpect (status (). isOk ()). ogExpect (jsonPath ("$", hasSize (1))). og Expect ( jsonPath ("$ [0] .name", er (alex.getName ()))); }

Det få(…) metodeopkald kan erstattes af andre metoder, der svarer til HTTP-verber som f.eks sætte(), stolpe()osv. Bemærk venligst, at vi også indstiller indholdstypen i anmodningen.

MockMvc er fleksibel, og vi kan oprette enhver anmodning ved hjælp af den.

7. Integrationstestning med @SpringBootTest

Som navnet antyder, fokuserer integrationstests på at integrere forskellige lag i applikationen. Det betyder også, at der ikke er spottet involveret.

Ideelt set skal vi holde integrationstestene adskilt fra enhedstestene og ikke køre sammen med enhedstestene. Vi kan gøre dette ved at bruge en anden profil til kun at køre integrationstestene. Et par grunde til at gøre dette kan være, at integrationstestene er tidskrævende og muligvis har brug for en egentlig database for at udføre.

Men i denne artikel fokuserer vi ikke på det, og vi bruger i stedet H2-persistenslagring i hukommelsen.

Integrationstestene skal starte en container for at udføre testsagerne. Derfor er der behov for en vis ekstra opsætning til dette - alt dette er let i Spring Boot:

@RunWith (SpringRunner.class) @SpringBootTest (SpringBootTest.WebEnvironment.MOCK, classes = Application.class) @AutoConfigureMockMvc @TestPropertySource (locations = "classpath: application-integrationtest.properties") offentlig klasse EmployeeRestControllerIntegrationTechnologyIntegration; @Autowired privat EmployeeRepository repository; // skriv testcases her}

Det @SpringBootTest annotering er nyttig, når vi har brug for at bootstrape hele containeren. Annotationen fungerer ved at oprette ApplicationContext der vil blive brugt i vores tests.

Vi kan bruge webMiljø attribut for @SpringBootTest at konfigurere vores runtime-miljø vi bruger WebMiljø.MOCK her, så beholderen fungerer i et mock-servlet-miljø.

Dernæst @TestPropertySource annotation hjælper med at konfigurere placeringen af ​​egenskabsfiler, der er specifikke for vores tests. Bemærk, at ejendomsfilen indlæst med @TestPropertySource vil tilsidesætte det eksisterende application.properties fil.

Det application-integrationtest.properties indeholder detaljerne til konfiguration af persistenslagring:

spring.datasource.url = jdbc: h2: mem: test spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.H2Dialect

Hvis vi vil køre vores integrationstest mod MySQL, kan vi ændre ovenstående værdier i egenskabsfilen.

Testcases for integrationstestene kan se ud som Controller lag enhedstest:

@ Test offentlig ugyldighed givenEmployees_whenGetEmployees_thenStatus200 () kaster undtagelse {createTestEmployee ("bob"); mvc.perform (get ("/ api / workers") .contentType (MediaType.APPLICATION_JSON)). og Expect (status (). isOk ()). og Expp ​​(content () .contentTypeCompatibleWith (MediaType.APPLICATION_JSON)) .andExpect (jsonPath ("$ [0] .navn", er ("bob"))); }

Forskellen fra Controller lagsenhedstest er, at her spottes intet, og end-to-end-scenarier vil blive udført.

8. Autokonfigurerede tests

En af de fantastiske funktioner i Spring Boot's automatisk konfigurerede kommentarer er, at det hjælper med at indlæse dele af den komplette applikation og testspecifikke lag af kodebasen.

Ud over de ovennævnte kommentarer er her en liste over et par meget anvendte kommentarer:

  • @WebFluxTest: Vi kan bruge @WebFluxTest kommentar til at teste Spring WebFlux-controllere. Det bruges ofte sammen med @MockBean at levere mock-implementeringer til krævede afhængigheder.
  • @JdbcTest: We kan bruge @JdbcTest kommentar til at teste JPA-applikationer, men det er til test, der kun kræver en Datakilde. Annotationen konfigurerer en integreret database i hukommelsen og en JdbcTemplate.
  • @JooqTest: For at teste jOOQ-relaterede tests kan vi bruge @JooqTest annotation, som konfigurerer en DSLContext.
  • @DataMongoTest: For at teste MongoDB-applikationer, @DataMongoTest er en nyttig kommentar. Som standard konfigurerer den en integreret MongoDB i hukommelsen, hvis driveren er tilgængelig gennem afhængigheder, konfigurerer en MongoTemplate, scanner efter @Dokument klasser og konfigurerer Spring Data MongoDB repositories.
  • @DataRedisTestgør det lettere at teste Redis-applikationer. Det scanner efter @RedisHash klasser og konfigurerer Spring Data Redis-arkiver som standard.
  • @DataLdapTest konfigurerer en integreret hukommelse LDAP (hvis tilgængelig), konfigurerer a LdapTemplate, scanner efter @Indgang klasser og konfigurerer Spring Data LDAP arkiver som standard.
  • @RestClientTest: Vi bruger generelt @RestClientTest kommentar for at teste REST-klienter. Det konfigurerer automatisk forskellige afhængigheder såsom Jackson, GSON og Jsonb support; konfigurerer en RestTemplateBuilder; og tilføjer støtte til MockRestServiceServer som standard.

9. Konklusion

I denne artikel dykkede vi dybt ned i teststøtten i Spring Boot og viste, hvordan man skriver enhedstest effektivt.

Den komplette kildekode til denne artikel kan findes på GitHub. Kildekoden indeholder mange flere eksempler og forskellige testsager.

Og hvis du vil fortsætte med at lære om test, har vi separate artikler relateret til integrationstest og enhedstest i JUnit 5.