Integratietesten in het voorjaar

1. Overzicht

Integratietesten spelen een belangrijke rol in de ontwikkelingscyclus van applicaties door het end-to-end gedrag van een systeem te verifiëren.

In dit artikel zullen we zien hoe we het Spring MVC-testraamwerk kunnen gebruiken om integratietests te schrijven en uit te voeren die controllers testen zonder expliciet een Servlet-container te starten.

2. Voorbereiding

De volgende Maven-afhankelijkheden zijn nodig voor het uitvoeren van integratietests, zoals beschreven in dit artikel. Eerst en vooral de nieuwste JUnit- en Spring-testafhankelijkheden:

 junit junit 4.12 test org.springframework spring-test 4.3.2.RELEASE test 

Voor het effectief bevestigen van resultaten, gaan we ook Hamcrest en JSON-pad gebruiken:

 org.hamcrest hamcrest-library 1.3 test com.jayway.jsonpath json-path 2.2.0 test 

3. Spring MVC-testconfiguratie

Laten we nu introduceren hoe u de Spring-enabled tests configureert en uitvoert.

3.1. Schakel Spring in Tests in

Ten eerste wordt elke Spring-enabled test uitgevoerd met behulp van @RunWith (SpringJUnit4ClassRunner.class); de hardloper is in wezen het startpunt om het Spring Test-framework te gaan gebruiken.

We hebben ook de @ContextConfiguration annotaties om de contextconfiguratie te laden en bootstrap de context die de test zal gebruiken.

Laten we eens kijken:

@RunWith (SpringJUnit4ClassRunner.class) @ContextConfiguration (classes = {ApplicationConfig.class}) @WebAppConfiguration openbare klasse GreetControllerIntegrationTest {....}

Merk op hoe, in @ContextConfiguration, wij hebben de ApplicationConfig.class config klasse die de configuratie laadt die we nodig hebben voor deze specifieke test.

We hebben hier een Java-configuratieklasse gebruikt om de contextconfiguratie te specificeren; op dezelfde manier kunnen we de op XML gebaseerde configuratie gebruiken:

@ContextConfiguration (locations = {""})

Ten slotte - de test is ook geannoteerd met @WebAppConfiguration - waarmee de context van de webapplicatie wordt geladen.

Standaard zoekt het naar de root-webtoepassing op het standaardpad src / main / webapp; de locatie kan worden overschreven door de waarde attribuut als:

@WebAppConfiguration (waarde = "")

3.2. De WebApplicationContext Voorwerp

WebApplicationContext (wac) biedt een webapplicatieconfiguratie. Het laadt alle applicatiebonen en controllers in de context.

We kunnen nu de context van de webapplicatie rechtstreeks in de test aansluiten:

@Autowired privé WebApplicationContext wac;

3.3. Spottende webcontextbonen

MockMvc biedt ondersteuning voor Spring MVC-testen. Het omvat alle bonen voor webtoepassingen en stelt ze beschikbaar voor testen.

Laten we eens kijken hoe we het kunnen gebruiken:

privé MockMvc mockMvc; @Before public void setup () gooit Uitzondering {this.mockMvc = MockMvcBuilders.webAppContextSetup (this.wac) .build (); }

We moeten het mockMvc object in het @Voordat geannoteerde methode, zodat we deze niet in elke test hoeven te initialiseren.

3.4. Controleer de testconfiguratie

Laten we voor onze zelfstudie hier controleren of we het WebApplicationContext voorwerp (wac) naar behoren. We zullen ook verifiëren dat het juist is servletContext wordt bijgevoegd:

@Test openbare leegte gegevenWac_whenServletContext_thenItProvidesGreetController () {ServletContext servletContext = wac.getServletContext (); Assert.assertNotNull (servletContext); Assert.assertTrue (servletContext-instantie van MockServletContext); Assert.assertNotNull (wac.getBean ("greetController")); }

Merk op dat we ook controleren of we een GreetController.java bean bestaat in de webcontext - wat ervoor zorgt dat lente-bonen correct worden geladen.

Op dit punt is het opzetten van de integratietest voltooid. Laten we eens kijken hoe we resourcemethoden kunnen testen met behulp van de MockMvc voorwerp.

4. Integratietests schrijven

In deze sectie bespreken we de basisbewerkingen die beschikbaar zijn via het testraamwerk.

We laten zien hoe u verzoeken met padvariabelen en parameters kunt verzenden. We zullen ook volgen met de paar voorbeelden die laten zien hoe u kunt beweren dat de juiste weergavenaam is omgezet of dat de antwoordtekst is zoals verwacht.

De volgende fragmenten gebruiken statische invoer uit MockMvcRequestBuilders of MockMvcResultMatchers klassen.

4.1. Controleer de weergavenaam

Laten we de /Startpagina eindpunt van onze test als:

// localhost: 8080 / spring-mvc-test /

of

// localhost: 8080 / spring-mvc-test / homePage

Codefragment:

@Test openbare ongeldige gegevenHomePageURI_whenMockMVC_thenReturnsIndexJSPViewName () {this.mockMvc.perform (get ("/ homePage")). AndDo (print ()) .andExpect (view (). Name ("index")); }

Laten we dat opsplitsen:

  • uitvoeren() methode zal een get-verzoekmethode aanroepen die de Resultaatacties. Met behulp van dit resultaat kunnen we assertieverwachtingen hebben over de respons, zoals inhoud, HTTP-status, koptekst, enz
  • andDo (print ()) zal het verzoek en de reactie afdrukken. Dit is handig om een ​​gedetailleerd overzicht te krijgen in geval van een fout
  • en verwachten ()verwacht het opgegeven argument. In ons geval verwachten we dat "index" wordt geretourneerd via MockMvcResultMatchers.view ()

4.2. Controleer antwoordtekst

We zullen aanroepen /begroeten eindpunt van onze test als:

// localhost: 8080 / spring-mvc-test / greet

Verwachte resultaten:

{"id": 1, "message": "Hallo wereld !!!" }

Codefragment:

@Test openbare ongeldig gegevenGreetURI_whenMockMVC_thenVerifyResponse () {MvcResult mvcResult = this.mockMvc.perform (get ("/ greet")) .andDo (print ()). AndExpect (status (). IsOk ()) .andExpect (jsonPath ("$ .message "). value (" Hallo wereld !!! ")) .andReturn (); Assert.assertEquals ("application / json; charset = UTF-8", mvcResult.getResponse (). GetContentType ()); }

Laten we eens kijken wat er precies aan de hand is:

  • andExpect (MockMvcResultMatchers.status (). isOk ())zal controleren of de HTTP-status van het antwoord is OK d.w.z. 200. Dit zorgt ervoor dat het verzoek met succes is uitgevoerd
  • andExpect (MockMvcResultMatchers.jsonPath ("$. bericht"). waarde ("Hallo wereld !!!")) controleert of de inhoud van het antwoord overeenkomt met het argument "Hallo Wereld!!!“. Hier gebruikten we jsonPath die de responsinhoud extraheert en de gevraagde waarde levert
  • En terugkomen()retourneert de MvcResult object dat wordt gebruikt wanneer we iets moeten verifiëren dat niet haalbaar is door de bibliotheek. U kunt zien dat we hebben toegevoegd assertEquals om overeen te komen met het inhoudstype van het antwoord dat is geëxtraheerd uit het MvcResult voorwerp

4.3. Verzenden KRIJGEN Verzoek met padvariabele

We zullen aanroepen / greetWithPathVariable / {naam} eindpunt van onze test als:

// localhost: 8080 / spring-mvc-test / greetWithPathVariable / John

Verwachte resultaten:

{"id": 1, "message": "Hallo wereld John !!!" }

Codefragment:

@Test openbare leegte gegevenGreetURIWithPathVariable_whenMockMVC_thenResponseOK () {this.mockMvc .perform (get ("/ greetWithPathVariable / {naam}", "John")) .andDo (print ()). AndExpect (status (). IsOk ()) .andExpect (content (). contentType ("application / json; charset = UTF-8")) .andExpect (jsonPath ("$. message"). value ("Hello World John !!!")); }

MockMvcRequestBuilders.get ("/ greetWithPathVariable / {name}", "John") zal het verzoek verzenden als '/ greetWithPathVariable / John“.

Dit wordt gemakkelijker met betrekking tot leesbaarheid en weten wat de parameters zijn die dynamisch in de URL worden ingesteld. Merk op dat we zoveel padparameters kunnen doorgeven als nodig is.

4.4. Sturen KRIJGEN Verzoek met queryparameters

We zullen een beroep doen / greetWithQueryVariable? name = {naam} eindpunt van onze test als:

// localhost: 8080 / spring-mvc-test / greetWithQueryVariable? name = John% 20Doe

Verwachte resultaten:

{"id": 1, "message": "Hallo wereld John Doe !!!" }

Codefragment:

@Test openbare leegte gegevenGreetURIWithQueryParameter_whenMockMVC_thenResponseOK () {this.mockMvc.perform (get ("/ greetWithQueryVariable") .param ("name", "John Doe")). AndDo (print ()). AndExpect (status (). IsOk ( )) .andExpect (content (). contentType ("application / json; charset = UTF-8")) .andExpect (jsonPath ("$. message"). value ("Hallo wereld John Doe !!!")); }

param ("naam", "John Doe") zal de queryparameter in het KRIJGEN verzoek. Het lijkt op " / greetWithQueryVariable? name = John% 20Doe“.

De queryparameter kan ook worden geïmplementeerd met behulp van de URI-sjabloonstijl:

this.mockMvc.perform (get ("/ greetWithQueryVariable? name = {name}", "John Doe"));

4.5. Sturen POST Verzoek

We zullen aanroepen / greetWithPost eindpunt van onze test als:

// localhost: 8080 / spring-mvc-test / greetWithPost

Verwachte resultaten:

{"id": 1, "message": "Hallo wereld !!!" }

Codefragment:

@Test openbare leegte gegevenGreetURIWithPost_whenMockMVC_thenVerifyResponse () {this.mockMvc.perform (post ("/ greetWithPost")). AndDo (print ()) .andExpect (status (). IsOk ()). AndExpect (content () .contentType (" application / json; charset = UTF-8 ")) .andExpect (jsonPath (" $. message "). value (" Hello World !!! ")); }

MockMvcRequestBuilders.post ("/ greetWithPost") zal het postverzoek verzenden. Padvariabelen en queryparameters kunnen op dezelfde manier worden ingesteld als we eerder keken, terwijl formuliergegevens kunnen worden ingesteld via param () methode alleen vergelijkbaar met Queryparameter als:

// localhost: 8080 / spring-mvc-test / greetWithPostAndFormData

Formuliergegevens:

id = 1; naam = John% 20Doe

Verwachte resultaten:

{"id": 1, "message": "Hallo wereld John Doe !!!" }

Codefragment:

@Test openbare leegte gegevenGreetURIWithPostAndFormData_whenMockMVC_thenResponseOK () {this.mockMvc.perform (post ("/ greetWithPostAndFormData"). Param ("id", "1") .param ("name", "John Doe")). AndDo (print ( )). andExpect (status (). isOk ()) .andExpect (content (). contentType ("application / json; charset = UTF-8")) .andExpect (jsonPath ("$. message"). value (" Hallo wereld John Doe !!! ")) .andExpect (jsonPath (" $. Id "). Waarde (1)); }

In het bovenstaande codefragment hebben we twee parameters toegevoegd, id als "1" en naam als "John Doe".

5. MockMvc Beperkingen

MockMvc biedt een elegante en gebruiksvriendelijke API om webeindpunten aan te roepen en tegelijkertijd hun reactie te inspecteren en te bevestigen. Ondanks al zijn voordelen heeft het een paar beperkingen.

Allereerst gebruikt het een subklasse van de DispatcherServlet om testverzoeken af ​​te handelen. Om specifieker te zijn, de TestDispatcherServlet is verantwoordelijk voor het oproepen van controllers en het uitvoeren van alle bekende lentemagie.

De MockMvc klasse verpakt dit TestDispatcherServlet intern. Dus elke keer dat we een verzoek verzenden met de uitvoeren() methode, MockMvc zal de onderliggende gebruiken TestDispatcherServlet direct. Daarom er zijn geen echte netwerkverbindingen gemaakt, en bijgevolg zullen we niet de hele netwerkstack testen tijdens het gebruik MockMvc.

Ook,omdat Spring een nep-webtoepassingscontext voorbereidt om de HTTP-verzoeken en -antwoorden te bespotten, ondersteunt het mogelijk niet alle functies van een volledige Spring-toepassing.

Deze nep-setup ondersteunt bijvoorbeeld geen HTTP-omleidingen. Dit lijkt in eerste instantie misschien niet zo belangrijk. Spring Boot lost echter enkele fouten op door het huidige verzoek om te leiden naar het /fout eindpunt. Dus als we de MockMvc, we kunnen sommige API-fouten mogelijk niet testen.

Als alternatief voor MockMvc, we kunnen een meer reële toepassingscontext opzettenen gebruik dan RestTemplate of zelfs wees gerust om onze applicatie te testen.

Dit is bijvoorbeeld eenvoudig met Spring Boot:

@RunWith (SpringRunner.class) @SpringBootTest (webEnvironment = RANDOM_PORT) openbare klasse GreetControllerRealIntegrationTest {@LocalServerPort privé int-poort; @Before public void setUp () {RestAssured.port = poort; } @Test openbare leegte gegevenGreetURI_whenSendingReq_thenVerifyResponse () {gegeven (). Get ("/ greet") .then () .statusCode (200); }}

Op deze manier zal elke test een echt HTTP-verzoek doen aan de applicatie die luistert op een willekeurige TCP-poort.

6. Conclusie

In deze tutorial hebben we een paar eenvoudige Spring-enabled integratietests geïmplementeerd.

We hebben ook gekeken naar de WebApplicationContext en MockMVC objectcreatie die een belangrijke rol speelde bij het aanroepen van de eindpunten van de applicatie.

Als we verder kijken, hebben we besproken hoe we kunnen verzenden KRIJGEN en POST verzoeken met variaties in het doorgeven van parameters en hoe de HTTP-antwoordstatus, header en inhoud kunnen worden geverifieerd.

Als afsluitende opmerking hebben we ook enkele beperkingen van het MockMvc. Als we die beperkingen kennen, kunnen we een weloverwogen beslissing nemen over hoe we onze tests gaan uitvoeren.

Ten slotte is de implementatie van al deze voorbeelden en codefragmenten beschikbaar in GitHub.