Testen in Spring Boot

1. Overzicht

In deze tutorial zullen we kijken naar het schrijven van tests met behulp van de framework-ondersteuning in Spring Boot. We behandelen unit-tests die afzonderlijk kunnen worden uitgevoerd, evenals integratietests die de Spring-context opstarten voordat tests worden uitgevoerd.

Als je nieuw bent bij Spring Boot, bekijk dan onze introductie tot Spring Boot.

2. Projectconfiguratie

De applicatie die we in dit artikel gaan gebruiken, is een API die enkele basisbewerkingen biedt op een Werknemer Bron. Dit is een typische gelaagde architectuur: de API-aanroep wordt verwerkt vanuit het Controller naar Onderhoud naar de Volharding laag.

3. Maven afhankelijkheden

Laten we eerst onze testafhankelijkheden toevoegen:

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

De spring-boot-starter-test is de primaire afhankelijkheid die de meeste elementen bevat die nodig zijn voor onze tests.

De H2 DB is onze in-memory database. Het elimineert de noodzaak voor het configureren en starten van een daadwerkelijke database voor testdoeleinden.

4. Integratietesten met @BuienRadarNL

We gaan werken met een entiteit met de naam Werknemer, die een ID kaart en een naam als zijn eigenschappen:

@Entity @Table (naam = "persoon") openbare klasse Werknemer {@Id @GeneratedValue (strategie = GenerationType.AUTO) privé Lange id; @Size (min = 3, max = 20) private String-naam; // standaard getters en setters, constructeurs}

En hier is onze repository met Spring Data JPA:

@Repository openbare interface EmployeeRepository breidt JpaRepository uit {openbare werknemer findByName (tekenreeksnaam); }

Dat is het voor de persistentielaagcode. Laten we nu onze testles gaan schrijven.

Laten we eerst het skelet van onze testklasse maken:

@RunWith (SpringRunner.class) @DataJpaTest openbare klasse EmployeeRepositoryIntegrationTest {@Autowired privé TestEntityManager entityManager; @Autowired privé EmployeeRepository employeeRepository; // schrijf hier testcases}

@RunWith (SpringRunner.class) biedt een brug tussen Spring Boot-testfuncties en JUnit. Elke keer dat we Spring Boot-testfuncties gebruiken in onze JUnit-tests, is deze annotatie vereist.

@BuienRadarNL biedt een aantal standaardinstellingen die nodig zijn voor het testen van de persistentielaag:

  • het configureren van H2, een in-memory database
  • Slaapstand, Spring Data en de Databron
  • uitvoeren van een @EntityScan
  • SQL-logboekregistratie inschakelen

Om DB-bewerkingen uit te voeren, hebben we enkele records nodig die al in onze database staan. Om deze gegevens in te stellen, kunnen we gebruik maken van TestEntityManager.

De Spring Boot TestEntityManager is een alternatief voor de standaard JPA EntityManager die methoden biedt die vaak worden gebruikt bij het schrijven van tests.

WerknemerRepository is het onderdeel dat we gaan testen.

Laten we nu onze eerste testcase schrijven:

@Test openbare leegte whenFindByName_thenReturnEmployee () {// gegeven werknemer alex = nieuwe werknemer ("alex"); entiteitManager.persist (alex); entiteitManager.flush (); // wanneer werknemer gevonden = employeeRepository.findByName (alex.getName ()); // dan assertThat (found.getName ()) .isEqualTo (alex.getName ()); }

In de bovenstaande test gebruiken we de TestEntityManager om een Werknemer in de database en het lezen via de zoek op naam API.

De assertThat (...) deel komt uit de Assertj-bibliotheek, die gebundeld wordt met Spring Boot.

5. Bespotten met @RTLnieuws

Onze Onderhoud laagcode is afhankelijk van onze Opslagplaats.

Om het Onderhoud laag, hoeven we niet te weten of ons zorgen maakt over hoe de persistentielaag wordt geïmplementeerd:

@Service openbare klasse EmployeeServiceImpl implementeert EmployeeService {@Autowired privé EmployeeRepository employeeRepository; @Override openbare werknemer getEmployeeByName (String naam) {return employeeRepository.findByName (naam); }}

Idealiter zouden we in staat moeten zijn om onze Onderhoud laagcode zonder bedrading in onze volledige persistentielaag.

Om dit te behalen, we kunnen de spottende ondersteuning van Spring Boot Test gebruiken.

Laten we eerst het skelet van de testklasse bekijken:

@RunWith (SpringRunner.class) openbare klasse EmployeeServiceImplIntegrationTest {@TestConfiguration statische klasse EmployeeServiceImplTestContextConfiguration {@Bean openbare EmployeeService employeeService () {retourneer nieuwe EmployeeServiceImpl (); }} @Autowired privé EmployeeService employeeService; @MockBean privé EmployeeRepository employeeRepository; // schrijf hier testcases}

Om het Onderhoud class, hebben we een instantie nodig van de Onderhoud klasse gemaakt en beschikbaar als een @Boon zodat we kunnen @Autowire het in onze testklasse. We kunnen deze configuratie bereiken met behulp van de @TestConfiguration annotatie.

Tijdens het scannen van componenten kunnen we ontdekken dat componenten of configuraties die alleen voor specifieke tests zijn gemaakt, per ongeluk overal worden opgepikt. Om dit te helpen voorkomen, Spring Boot biedt de @TestConfiguration annotatie die we kunnen toevoegen aan klassen in src / test / java om aan te geven dat ze niet mogen worden gescand.

Een ander interessant ding hier is het gebruik van @RTLnieuws. Het creëert een mock voor de WerknemerRepository, die kan worden gebruikt om de oproep naar de werkelijke WerknemerRepository:

@Before public void setUp () {Werknemer alex = nieuwe werknemer ("alex"); Mockito.when (employeeRepository.findByName (alex.getName ())) .thenReturn (alex); }

Aangezien de installatie is voltooid, zal de testcase eenvoudiger zijn:

@Test openbaar ongeldig whenValidName_thenEmployeeShouldBeFound () {String name = "alex"; Werknemer gevonden = employeeService.getEmployeeByName (naam); assertThat (found.getName ()) .isEqualTo (naam); }

6. Eenheid testen met @WebMvcTest

Onze Controller hangt af van de Onderhoud laag; laten we voor de eenvoud slechts één enkele methode toevoegen:

@RestController @RequestMapping ("/ api") openbare klasse EmployeeRestController {@Autowired privé EmployeeService employeeService; @GetMapping ("/ workers") openbare lijst getAllEmployees () {return employeeService.getAllEmployees (); }}

Omdat we alleen gefocust zijn op de Controller code, het is normaal om de Onderhoud laagcode voor onze unit tests:

@RunWith (SpringRunner.class) @WebMvcTest (EmployeeRestController.class) openbare klasse EmployeeRestControllerIntegrationTest {@Autowired private MockMvc mvc; @MockBean privé EmployeeService-service; // schrijf hier testcases}

Om het Controllers, we kunnen gebruiken @WebMvcTest. Het zal de Spring MVC-infrastructuur automatisch configureren voor onze unit-tests.

In de meeste gevallen, @WebMvcTest zal worden beperkt tot het opstarten van een enkele controller. We kunnen het ook samen met gebruiken @RTLnieuws om nepimplementaties te bieden voor alle vereiste afhankelijkheden.

@WebMvcTest configureert ook automatisch MockMvc, die een krachtige manier biedt om eenvoudig MVC-controllers te testen zonder een volledige HTTP-server te starten.

Dat gezegd hebbende, laten we onze testcase schrijven:

@Test openbare ongeldigheid gegevenEmployees_whenGetEmployees_thenReturnJsonArray () gooit uitzondering {werknemer alex = nieuwe werknemer ("alex"); Lijst allEmployees = Arrays.asList (alex); gegeven (service.getAllEmployees ()). willReturn (allEmployees); mvc.perform (get ("/ api / workers") .contentType (MediaType.APPLICATION_JSON)) .andExpect (status (). isOk ()) .andExpect (jsonPath ("$", hasSize (1))) .andExpect ( jsonPath ("$ [0] .name", is (alex.getName ()))); }

De krijgen(…) method-aanroep kan worden vervangen door andere methoden die overeenkomen met HTTP-werkwoorden zoals leggen(), post(), enz. Houd er rekening mee dat we ook het inhoudstype in het verzoek instellen.

MockMvc is flexibel en we kunnen er elk verzoek mee aanmaken.

7. Integratietesten met @BuienRadarNL

Zoals de naam al doet vermoeden, richten integratietests zich op het integreren van verschillende lagen van de applicatie. Dat betekent ook dat er geen sprake is van spot.

Idealiter zouden we de integratietests gescheiden moeten houden van de unit-tests en niet samen met de unit-tests moeten lopen. We kunnen dit doen door een ander profiel te gebruiken om alleen de integratietests uit te voeren. Een aantal redenen om dit te doen, kunnen zijn dat de integratietests tijdrovend zijn en mogelijk een echte database nodig hebben om uit te voeren.

In dit artikel zullen we ons daar echter niet op concentreren en in plaats daarvan gebruikmaken van de H2-persistentieopslag in het geheugen.

De integratietests moeten een container opstarten om de testcases uit te voeren. Daarom is hiervoor een aantal extra instellingen nodig - dit is allemaal eenvoudig in Spring Boot:

@RunWith (SpringRunner.class) @SpringBootTest (SpringBootTest.WebEnvironment.MOCK, klassen = Application.class) @AutoConfigureMockMvc @TestPropertySource (locations = "classpath: application-integrationtest.properties") openbare klasse EmployeeRestController privé Mockutowest; @Autowired privé EmployeeRepository-repository; // schrijf hier testcases}

De @BuienRadarNL annotatie is handig wanneer we de hele container moeten bootstrap. De annotatie werkt door het maken van de ApplicationContext die zullen worden gebruikt in onze tests.

We kunnen de webomgeving kenmerk van @BuienRadarNL om onze runtime-omgeving te configureren; we gebruiken WebEnvironment.MOCK hier zodat de container zal werken in een mock-servlet-omgeving.

Vervolgens de @TestPropertySource annotatie helpt bij het configureren van de locaties van eigenschappenbestanden die specifiek zijn voor onze tests. Merk op dat het eigenschappenbestand geladen met @TestPropertySource zal het bestaande overschrijven application.properties het dossier.

De application-integrationtest.properties bevat de details om de persistentieopslag te configureren:

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

Als we onze integratietests tegen MySQL willen uitvoeren, kunnen we de bovenstaande waarden in het eigenschappenbestand wijzigen.

De testcases voor de integratietests lijken mogelijk op de Controller laageenheidstests:

@Test openbare ongeldigheid gegevenEmployees_whenGetEmployees_thenStatus200 () gooit uitzondering {createTestEmployee ("bob"); mvc.perform (get ("/ api / workers") .contentType (MediaType.APPLICATION_JSON)) .andExpect (status (). isOk ()) .andExpect (content () .contentTypeCompatibleWith (MediaType.APPLICATION_JSON)) .andExpect (jsonPath ("$ [0] .name", is ("bob"))); }

Het verschil met de Controller Layer unit tests is dat hier niets wordt bespot en end-to-end scenario's worden uitgevoerd.

8. Automatisch geconfigureerde tests

Een van de verbazingwekkende kenmerken van de automatisch geconfigureerde annotaties van Spring Boot is dat het helpt om delen van de volledige applicatie en testspecifieke lagen van de codebase te laden.

Naast de bovengenoemde annotaties is hier een lijst met enkele veelgebruikte annotaties:

  • @WebFluxTest: We kunnen de @WebFluxTest annotatie om Spring WebFlux-controllers te testen. Het wordt vaak samen met @RTLnieuws om nepimplementaties te bieden voor vereiste afhankelijkheden.
  • @JdbcTest: W.We kunnen de @JDbcTest annotatie om JPA-applicaties te testen, maar het is voor tests die alleen een Databron. De annotatie configureert een ingebedde database in het geheugen en een JdbcTemplate.
  • @JooqTest: Om jOOQ-gerelateerde tests te testen, kunnen we gebruiken @JooqTest annotatie, die een DSLContext configureert.
  • @BuienRadarNL: Om MongoDB-toepassingen te testen, @BuienRadarNL is een nuttige aantekening. Standaard configureert het een in het geheugen ingebedde MongoDB als de driver beschikbaar is via afhankelijkheden, configureert een Mongo-sjabloon, zoekt naar @Document klassen en configureert Spring Data MongoDB-opslagplaatsen.
  • @BuienRadarNLmaakt het gemakkelijker om Redis-toepassingen te testen. Het zoekt naar @RedisHash klassen en configureert Spring Data Redis-opslagplaatsen standaard.
  • @DataLdapTest configureert een ingebed in het geheugen LDAP (indien beschikbaar), configureert een LdapTemplate, zoekt naar @Entry klassen en configureert Spring Data LDAP repositories standaard.
  • @RTLnieuws: We gebruiken over het algemeen de @RTLnieuws annotatie om REST-clients te testen. Het configureert automatisch verschillende afhankelijkheden, zoals Jackson, GSON en Jsonb-ondersteuning; configureert een RestTemplateBuilder; en voegt ondersteuning toe voor MockRestServiceServer standaard.

9. Conclusie

In dit artikel hebben we dieper ingegaan op de testondersteuning in Spring Boot en hebben we laten zien hoe je unit-tests efficiënt kunt schrijven.

De volledige broncode van dit artikel is te vinden op GitHub. De broncode bevat nog veel meer voorbeelden en verschillende testcases.

En als u wilt blijven leren over testen, hebben we afzonderlijke artikelen met betrekking tot integratietests en unit-tests in JUnit 5.