Ohjelmointi

JUnit 5 -opetusohjelma, osa 2: Yksikön testausjousi MVC JUnit 5: n kanssa

Kevään MVC on yksi suosituimmista Java-kehyksistä yritys Java-sovellusten rakentamiseen, ja se soveltuu erittäin hyvin testaamiseen. Suunnittelullaan Spring MVC edistää huolenaiheiden erottamista ja kannustaa koodaamaan rajapintoja vastaan. Nämä ominaisuudet tekevät yhdessä Springin riippuvuusinjektioiden kanssa kevään sovellukset erittäin testattavissa.

Tämä opetusohjelma on toinen puoli johdannostani yksikötestaukseen JUnit 5: llä. Näytän sinulle, kuinka JUnit 5 integroidaan Springiin, ja sitten esitän sinulle kolme työkalua, joiden avulla voit testata Spring MVC -ohjaimia, -palveluja ja -tietovarastoja.

lataa Hae koodi Lataa lähdekoodi esimerkiksi tässä opetusohjelmassa käytetyistä sovelluksista. Luonut Steven Haines JavaWorldille.

JUnit 5 integroidaan jouseen 5

Tässä opetusohjelmassa käytämme Mavenia ja Spring Bootia, joten ensimmäinen asia, joka meidän on tehtävä, on lisätä JUnit 5 -riippuvuus Maven POM -tiedostoon:

  org.junit.jupiter junit-jupiter 5.6.0 -testi 

Aivan kuten osassa 1, käytämme tässä esimerkissä Mockitoa. Joten meidän on lisättävä JUnit 5 Mockito -kirjasto:

  org.mockito mockito-junit-jupiter 3.2.4 -testi 

@ExtendWith ja SpringExtension-luokka

JUnit 5 määrittelee laajennusliittymä, jonka kautta luokat voivat integroitua JUnit-testeihin suorituksen elinkaaren eri vaiheissa. Voimme ottaa laajennukset käyttöön lisäämällä @ExtendWith huomautus testiluokkiin ja määritettävä ladattava laajennusluokka. Laajennus voi tällöin toteuttaa useita takaisinsoittorajapintoja, joita käytetään koko testin elinkaaren ajan: ennen kuin kaikki testit suoritetaan, ennen kutakin testiä, jokaisen testiajon jälkeen ja kun kaikki testit on suoritettu.

Kevät määrittelee a KevätLaajennus luokka, joka tilaa JUnit 5: n elinkaari-ilmoitukset "testikontekstin" luomiseksi ja ylläpitämiseksi. Muistathan, että Springin sovelluskonteksti sisältää kaikki kevätpavut sovelluksessa ja että se suorittaa riippuvuusinjektion yhdistääkseen sovelluksen ja sen riippuvuudet. Spring käyttää JUnit 5 -laajennusmallia ylläpitääkseen testin sovelluskontekstia, mikä tekee Spring-yksikön kirjoittamisen yksinkertaisesta.

Kun olemme lisänneet JUnit 5 -kirjaston Maven POM -tiedostoon, voimme käyttää SpringExtension.luokka JUnit 5 -testiluokkien laajentamiseksi:

 @ExtendWith (SpringExtension.class) -luokan MyTests {// ...}

Tässä tapauksessa esimerkki on Spring Boot -sovellus. Onneksi @SpringBootTest merkinnässä on jo @ExtendWith (SpringExtension.class) merkinnät, joten meidän tarvitsee vain sisällyttää @SpringBootTest.

Lisätään Mockito-riippuvuus

Testataksemme jokaisen komponentin kunnolla erillään ja simuloidaksemme erilaisia ​​skenaarioita, haluamme luoda jokaisen luokan riippuvuuksien pilkkaavat toteutukset. Tässä on Mockito. Sisällytä seuraava riippuvuus POM-tiedostoon lisätäksesi tukea Mockitolle:

  org.mockito mockito-junit-jupiter 3.2.4 -testi 

Kun olet integroinut JUnit 5: n ja Mockito-sovelluksen Spring-sovellukseesi, voit hyödyntää Mockitoa määrittämällä yksinkertaisesti Spring-pavun (kuten palvelun tai arkiston) testiluokassasi käyttämällä @MockBean merkintä. Tässä on esimerkkimme:

 @SpringBootTest julkisen luokan WidgetServiceTest {/ ** * Autowire palvelussa, jonka haluamme testata * / @Autowired private WidgetService -palvelu; / ** * Luo WidgetRepository-mallin toteutus * / @MockBean private WidgetRepository -tietovarasto; ...} 

Tässä esimerkissä luomme pilkkaa WidgetRepository sisällä meidän WidgetServiceTest luokassa. Kun kevät näkee tämän, se yhdistää sen automaattisesti meidän WidgetService jotta voimme luoda erilaisia ​​skenaarioita testimenetelmissämme. Kukin testimenetelmä määrittää laitteen käyttäytymisen WidgetRepository, esimerkiksi palauttamalla pyydetyt Widget tai palauttamalla Valinnainen. Tyhjä () kyselylle, jolle tietoja ei löydy. Vietämme loppuosan tästä opetusohjelmasta tarkastelemalla esimerkkejä näiden tapojen konfiguroimisesta.

Kevään MVC-esimerkkisovellus

Kevätpohjaisten yksikkötestien kirjoittamiseen tarvitsemme sovelluksen, jolla niitä voidaan kirjoittaa. Onneksi voimme käyttää minun esimerkkisovellusta Kevät-sarja opetusohjelma "Mastering Spring framework 5, Part 1: Spring MVC." Käytin kyseisen opetusohjelman esimerkkisovellusta perussovelluksena. Muutin sitä vahvemmalla REST-sovellusliittymällä, jotta meillä olisi vielä muutama asia testattavaksi.

Esimerkkisovellus on Spring MVC -verkkosovellus, jossa on REST-ohjain, palvelukerros ja arkisto, joka käyttää Spring Data JPA: ta "widgetien" säilyttämiseen H2-muistitietokantaan ja sieltä. Kuva 1 on yleiskatsaus.

Steven Haines

Mikä on widget?

A Widget on vain "juttu", jolla on tunnus, nimi, kuvaus ja versionumero. Tässä tapauksessa widgetimme merkitään JPA-merkinnöillä sen määrittelemiseksi kokonaisuudeksi. WidgetRestController on kevään MVC-ohjain, joka muuntaa RESTful-sovellusliittymäkutsut suoritettaviksi toimiksi Widgetit. WidgetService on tavallinen kevätpalvelu, joka määrittelee yrityksen toiminnallisuuden Widgetit. Lopuksi WidgetRepository on Spring Data JPA -rajapinta, jolle Spring luo toteutuksen ajon aikana. Tarkistamme kunkin luokan koodin kirjoittaessamme testejä seuraavissa osioissa.

Yksikkö testaa kevätpalvelua

Aloitetaan tarkistamalla, kuinka kevät testataanpalvelu, koska se on helpoin testata MVC-sovelluksessamme. Tämän osan esimerkit antavat meille mahdollisuuden tutkia JUnit 5: n integraatiota Springiin ottamatta käyttöön uusia testauskomponentteja tai kirjastoja, vaikka teemme sen myöhemmin opetusohjelmassa.

Aloitamme tarkistamalla WidgetService käyttöliittymä ja WidgetServiceImpl luokka, jotka näkyvät luetteloissa 1 ja 2.

Listaus 1. Spring-käyttöliittymä (WidgetService.java)

 paketti com.geekcap.javaworld.spring5mvcexample.service; tuo com.geekcap.javaworld.spring5mvcexample.model.Widget; tuo java.util.List; tuo java.util.Valinnainen; julkinen käyttöliittymä WidgetService {Valinnainen findById (pitkä id); Lista findAll (); Widget-tallennus (Widget-widget); void deleteById (pitkä tunnus); }

Listaus 2. Spring-palvelun toteutusluokka (WidgetServiceImpl.java)

 paketti com.geekcap.javaworld.spring5mvcexample.service; tuo com.geekcap.javaworld.spring5mvcexample.model.Widget; tuo com.geekcap.javaworld.spring5mvcexample.repository.WidgetRepository; tuo com.google.common.collect.Lists; tuo org.springframework.stereotype.Service; tuo java.util.ArrayList; tuo java.util.List; tuo java.util.Valinnainen; @Service public class WidgetServiceImpl toteuttaa WidgetService {private WidgetRepository -tietovaraston; public WidgetServiceImpl (WidgetRepository-arkisto) {this.repository = arkisto; } @Override public Valinnainen findById (Long id) {return repository.findById (id); } @Override public list findAll () {return Lists.newArrayList (repository.findAll ()); } @Override public Widget save (Widget -widget) {// Kasvata versionumeroa widget.setVersion (widget.getVersion () + 1); // Tallenna widget arkistoon return repository.save (widget); } @Override public void deleteById (Long id) {repository.deleteById (id); }}

WidgetServiceImpl on kevätpalvelu, johon on merkitty @Service merkintä, jolla on WidgetRepository johdotettu siihen rakentajansa kautta. findById (), findAll ()ja deleteById () menetelmät ovat kaikki läpäisymenetelmiä taustalla oleville WidgetRepository. Ainoa löytämäsi liiketoimintalogiikka sijaitsee Tallentaa() menetelmä, joka kasvattaa version versiota Widget kun se on tallennettu.

Testiluokka

Tämän luokan testaamiseksi meidän on luotava ja konfiguroitava pilkku WidgetRepository, johdin se WidgetServiceImpl ja johdot sitten WidgetServiceImpl testiluokkaan. Onneksi se on paljon helpompaa kuin miltä se kuulostaa. Luettelossa 3 näkyy WidgetServiceTest luokassa.

Listaus 3. Kevään huoltotestiluokka (WidgetServiceTest.java)

 paketti com.geekcap.javaworld.spring5mvcexample.service; tuo com.geekcap.javaworld.spring5mvcexample.model.Widget; tuo com.geekcap.javaworld.spring5mvcexample.repository.WidgetRepository; tuo org.junit.jupiter.api.Assertions; tuo org.junit.jupiter.api.DisplayName; tuo org.junit.jupiter.api.Test; tuo org.junit.jupiter.api.extension.ExtendWith; tuo org.springframework.beans.factory.annotation.Autowired; tuo org.springframework.boot.test.context.SpringBootTest; tuo org.springframework.boot.test.mock.mockito.MockBean; tuo org.springframework.test.context.junit.jupiter.SpringExtension; tuo java.util.Arrays; tuo java.util.List; tuo java.util.Valinnainen; tuo staattinen org.mockito.Mockito.doReturn; tuo staattinen org.mockito.ArgumentMatchers.any; @SpringBootTest julkisen luokan WidgetServiceTest {/ ** * Autowire palvelussa, jonka haluamme testata * / @Autowired private WidgetService -palvelu; / ** * Luo WidgetRepository-mallin toteutus * / @MockBean private WidgetRepository -tietovarasto; @Test @DisplayName ("Test findById Success") void testFindById () {// Asenna pilkkivarastomme Widget-widget = uusi Widget (1l, "Widgetin nimi", "Kuvaus", 1); doReturn (Valinnainen. (widget)). milloin (arkisto) .findById (1 l); // Suorita palvelupuhelu Valinnainen returnWidget = service.findById (1l); // Vahvista vastaus Assertions.assertTrue (returnWidget.isPresent (), "Widgetiä ei löytynyt"); Assertions.assertSame (returnWidget.get (), widget, "Palautettu widget ei ollut sama kuin pilkka"); } @Test @DisplayName ("Test findById Not Found") void testFindByIdNotFound () {// Asenna pilkkivarastomme doReturn (Valinnainen.empty ()). Milloin (arkisto) .findById (1l); // Suorita palvelupuhelu Valinnainen returnWidget = service.findById (1l); // Vahvista vastaus Assertions.assertFalse (returnWidget.isPresent (), "Widgetiä ei löydy"); } @Test @DisplayName ("Test findAll") void testFindAll () {// Asenna pilkkivarastomme Widget-widget1 = uusi Widget (1l, "Widgetin nimi", "Description", 1); Widget-widget2 = uusi widget (2l, "Widget 2 Name", "Description 2", 4); doReturn (Arrays.asList (widget1, widget2)). milloin (arkisto) .findAll (); // Suorita palvelupyynnön luettelo -widgetit = service.findAll (); // Vahvista vastaus Assertions.assertEquals (2, widgetit.size (), "findAllin pitäisi palauttaa 2 widgetiä"); } @Test @DisplayName ("Test save widget") void testSave () {// Asenna pilkkivarastomme Widget-widget = uusi Widget (1l, "Widgetin nimi", "Kuvaus", 1); doReturn (widget). milloin (arkisto) .save (mikä tahansa ()); // Suorita palvelupuhelu Widget ReturnWidget = service.save (widget); // Vahvista vastaus Assertions.assertNotNull (returnWidget, "Tallennetun widgetin ei tule olla tyhjä"); Assertions.assertEquals (2, returnWidget.getVersion (), "Versiota tulisi lisätä"); }} 

WidgetServiceTest luokka on merkitty @SpringBootTest merkintä, joka skannaa CLASSPATH kaikille Spring-kokoonpanoluokille ja papuille ja määrittää Spring-sovelluskontekstin testiluokalle. Ota huomioon, että WidgetServiceTest sisältää implisiittisesti myös @ExtendWith (SpringExtension.class) merkinnät @SpringBootTest merkintä, joka integroi testiluokan JUnit 5: een.

Testiluokassa käytetään myös Spring'sia @Autowired huomautus automaattiseen kirjoittamiseen a WidgetService testata vastaan, ja se käyttää Mockiton @MockBean merkintä mallin luomiseksi WidgetRepository. Tässä vaiheessa meillä on pilkka WidgetRepository jonka voimme määrittää, ja todellinen WidgetService pilkun kanssa WidgetRepository johdotettu siihen.

Testataan kevätpalvelua

Ensimmäinen testimenetelmä, testFindById (), suorittaa WidgetServiceon findById () menetelmä, jonka pitäisi palauttaa Valinnainen joka sisältää a Widget. Aloitamme luomalla a Widget että haluamme WidgetRepository palata. Sitten hyödynnämme Mockito-sovellusliittymää konfiguroimaan WidgetRepository :: findById menetelmä. Mock-logiikkamme rakenne on seuraava:

 doReturn (VALUE_TO_RETURN). kun (MOCK_CLASS_INSTANCE). MOCK_METHOD 

Tässä tapauksessa sanomme: Palauta an Valinnainen meidän Widget kun arkisto on findById () menetelmä kutsutaan argumentilla 1 (kuten a pitkä).

Seuraavaksi vetoamme WidgetServiceon findById menetelmä argumentilla 1. Vahvistamme sitten, että se on läsnä ja että palautettu Widget on se, jonka konfiguroimme pilkkaa WidgetRepository palata.