Programování

Výukový program JUnit 5, část 2: Testování jednotky Spring MVC s JUnit 5

Spring MVC je jedním z nejpopulárnějších rámců Java pro vytváření podnikových aplikací Java a k testování se velmi dobře hodí. Podle návrhu Spring MVC podporuje oddělení obav a podporuje kódování proti rozhraním. Tyto vlastnosti spolu s jarní implementací vkládání závislostí činí aplikace Spring velmi testovatelné.

Tento výukový program je druhou polovinou mého úvodu k testování jednotek s JUnit 5. Ukážu vám, jak integrovat JUnit 5 s Springem, poté vám představím tři nástroje, které můžete použít k testování Spring MVC řadičů, služeb a úložišť.

stáhnout Získat kód Stáhněte si zdrojový kód například aplikací použitých v tomto výukovém programu. Vytvořil Steven Haines pro JavaWorld.

Integrace JUnit 5 s pružinou 5

V tomto kurzu používáme Maven a Spring Boot, takže první věcí, kterou musíme udělat, je přidat závislost JUnit 5 do našeho souboru Maven POM:

  org.junit.jupiter test junit-jupiter 5.6.0 

Stejně jako v části 1 použijeme pro tento příklad Mockito. Budeme tedy muset přidat knihovnu JUnit 5 Mockito:

  org.mockito test mockito-junit-jupiter 3.2.4 

@ExtendWith a třída SpringExtension

JUnit 5 definuje rozšíření rozhraní, prostřednictvím kterých se mohou třídy integrovat s testy JUnit v různých fázích životního cyklu provádění. Rozšíření můžeme povolit přidáním @ExtendWith anotace k našim testovacím třídám a určení třídy rozšíření, která se má načíst. Rozšíření pak může implementovat různá rozhraní zpětného volání, která budou vyvolána po celou dobu životního cyklu testu: před spuštěním všech testů, před každým spuštěním testu, po každém spuštění testu a po spuštění všech testů.

Jaro definuje a SpringExtension třída, která se přihlásí k odběru oznámení o životním cyklu JUnit 5, aby vytvořila a udržovala „testovací kontext“. Připomeňme, že kontext aplikace Spring obsahuje všechny Spring fazole v aplikaci a že provádí vkládání závislostí, aby spojil aplikaci a její závislosti. Spring používá model rozšíření JUnit 5 k udržení kontextu aplikace testu, díky čemuž je psaní testů jednotek s Spring přímočaré.

Poté, co jsme do našeho souboru Maven POM přidali knihovnu JUnit 5, můžeme použít SpringExtension.class rozšířit naše testovací třídy JUnit 5:

 @ExtendWith (SpringExtension.class) třída MyTests {// ...}

Příkladem je v tomto případě aplikace Spring Boot. Naštěstí @SpringBootTest anotace již obsahuje @ExtendWith (SpringExtension.class) anotace, takže musíme pouze zahrnout @SpringBootTest.

Přidání závislosti Mockito

Abychom správně testovali každou komponentu v izolaci a simulovali různé scénáře, budeme chtít vytvořit simulované implementace závislostí každé třídy. Zde přichází místo Mockito. Chcete-li přidat podporu pro Mockito, zahrňte do souboru POM následující závislost:

  org.mockito test mockito-junit-jupiter 3.2.4 

Poté, co jste integrovali JUnit 5 a Mockito do své jarní aplikace, můžete využít Mockito jednoduše definováním Spring bean (jako je služba nebo úložiště) ve vaší testovací třídě pomocí @MockBean anotace. Zde je náš příklad:

 @SpringBootTest veřejná třída WidgetServiceTest {/ ** * Autowire ve službě, kterou chceme otestovat * / @Autowired soukromá služba WidgetService; / ** * Vytvořit simulovanou implementaci WidgetRepository * / @MockBean soukromé úložiště WidgetRepository; ...} 

V tomto příkladu vytváříme falešný výraz WidgetRepository uvnitř našeho WidgetServiceTest třída. Když to Spring uvidí, automaticky to zapojí do našeho WidgetService abychom mohli v našich testovacích metodách vytvářet různé scénáře. Každá zkušební metoda nakonfiguruje chování WidgetRepository, například vrácením požadovaného Widget nebo vrácení Optional.empty () pro dotaz, pro který nebyla nalezena data. Zbývající část tohoto kurzu strávíme zkoumáním příkladů různých způsobů konfigurace těchto falešných fazolí.

Ukázková aplikace Spring MVC

Abychom mohli psát jarní testy jednotek, potřebujeme aplikaci, která je zapíše proti. Naštěstí můžeme použít ukázkovou aplikaci z mého Jarní série tutoriál "Mastering Spring Framework 5, Part 1: Spring MVC." Ukázkovou aplikaci z tohoto tutoriálu jsem použil jako základní aplikaci. Upravil jsem to pomocí silnějšího REST API, abychom měli ještě pár věcí k testování.

Příkladem aplikace je webová aplikace Spring MVC s řadičem REST, vrstvou služeb a úložištěm, které používá Spring Data JPA k přetrvávání „widgetů“ do az databáze H2 v paměti. Obrázek 1 je přehled.

Steven Haines

Co je to widget?

A Widget je jen „věc“ s ID, jménem, ​​popisem a číslem verze. V tomto případě je náš widget opatřen anotacemi JPA, které jej definují jako entitu. The WidgetRestController je řadič Spring MVC, který převádí volání RESTful API na akce, na kterých se má provádět Widgety. The WidgetService je standardní jarní služba, která definuje obchodní funkce pro Widgety. Nakonec WidgetRepository je rozhraní Spring Data JPA, pro které Spring vytvoří implementaci za běhu. Během psaní testů v následujících částech zkontrolujeme kód pro každou třídu.

Jednotka testující jarní službu

Začněme kontrolou, jak otestovat jaroservis, protože to je nejjednodušší komponenta v naší aplikaci MVC k testování. Příklady v této části nám umožní prozkoumat integraci JUnit 5 s Springem bez zavedení nových testovacích komponent nebo knihoven, i když to uděláme později v tutoriálu.

Začneme kontrolou WidgetService rozhraní a WidgetServiceImpl třídy, které jsou uvedeny v seznamu 1 a 2.

Výpis 1. Rozhraní služby Spring (WidgetService.java)

 balíček com.geekcap.javaworld.spring5mvcexample.service; import com.geekcap.javaworld.spring5mvcexample.model.Widget; import java.util.List; import java.util.Optional; veřejné rozhraní WidgetService {Volitelné findById (Long id); Seznam findAll (); Uložení widgetu (Widget widget); void deleteById (Long id); }

Výpis 2. Třída implementace jarní služby (WidgetServiceImpl.java)

 balíček com.geekcap.javaworld.spring5mvcexample.service; import com.geekcap.javaworld.spring5mvcexample.model.Widget; import com.geekcap.javaworld.spring5mvcexample.repository.WidgetRepository; import com.google.common.collect.Lists; importovat org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.List; import java.util.Optional; @Service veřejná třída WidgetServiceImpl implementuje WidgetService {soukromé úložiště WidgetRepository; public WidgetServiceImpl (úložiště WidgetRepository) {this.repository = úložiště; } @Override public Volitelné findById (dlouhé id) {návratové úložiště.findById (id); } @Override public List findAll () {return Lists.newArrayList (repository.findAll ()); } @Override public Widget save (Widget widget) {// Zvýší číslo verze widget.setVersion (widget.getVersion () + 1); // Uložte widget do úložiště return repository.save (widget); } @Override public void deleteById (Long id) {repository.deleteById (id); }}

WidgetServiceImpl je jarní služba s poznámkami @Servis anotace, která má a WidgetRepository zapojen do něj prostřednictvím svého konstruktoru. The findById (), findAll (), a deleteById () metody jsou všechny passthrough metody k podkladovému WidgetRepository. Jediná obchodní logika, kterou najdete, se nachází v Uložit() metoda, která zvyšuje číslo verze souboru Widget když je uložen.

Testovací třída

Abychom mohli tuto třídu otestovat, musíme vytvořit a nakonfigurovat falešný WidgetRepository, zapojte jej do WidgetServiceImpl instance a poté připojte WidgetServiceImpl do naší testovací třídy. Naštěstí je to mnohem jednodušší, než to zní. Výpis 3 zobrazuje zdrojový kód pro WidgetServiceTest třída.

Výpis 3. Třída testovací služby Spring (WidgetServiceTest.java)

 balíček com.geekcap.javaworld.spring5mvcexample.service; import com.geekcap.javaworld.spring5mvcexample.model.Widget; import com.geekcap.javaworld.spring5mvcexample.repository.WidgetRepository; importovat org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; importovat org.junit.jupiter.api.extension.ExtendWith; importovat org.springframework.beans.factory.annotation.Autowired; importovat org.springframework.boot.test.context.SpringBootTest; importovat org.springframework.boot.test.mock.mockito.MockBean; importovat org.springframework.test.context.junit.jupiter.SpringExtension; import java.util.Arrays; import java.util.List; import java.util.Optional; importovat statický org.mockito.Mockito.doReturn; importovat statický org.mockito.ArgumentMatchers.any; @SpringBootTest veřejná třída WidgetServiceTest {/ ** * Autowire ve službě, kterou chceme otestovat * / @Autowired soukromá služba WidgetService; / ** * Vytvořit simulovanou implementaci WidgetRepository * / @MockBean soukromé úložiště WidgetRepository; @Test @DisplayName ("Test findById Success") void testFindById () {// Nastavení našeho falešného úložiště Widget widget = nový Widget (1l, "Název widgetu", "Popis", 1); doReturn (Optional.of (widget)). when (repository) .findById (1l); // Provedení servisního volání Volitelné returnedWidget = service.findById (1l); // Uplatněte odpověď Assertions.assertTrue (returnedWidget.isPresent (), "Widget nebyl nalezen"); Assertions.assertSame (returnedWidget.get (), widget, "Vrácený widget nebyl stejný jako falešný"); } @Test @DisplayName ("Test findById nebyl nalezen") void testFindByIdNotFound () {// Nastavení našeho falešného úložiště doReturn (Optional.empty ()). When (repository) .findById (1l); // Provedení servisního volání Volitelné returnedWidget = service.findById (1l); // Uplatněte odpověď Assertions.assertFalse (returnedWidget.isPresent (), "Widget by neměl být nalezen"); } @Test @DisplayName ("Test findAll") void testFindAll () {// Nastavení našeho falešného úložiště Widget widget1 = nový Widget (1l, "Název widgetu", "Popis", 1); Widget widget2 = nový Widget (2l, "Název widgetu 2", "Popis 2", 4); doReturn (Arrays.asList (widget1, widget2)). when (repository) .findAll (); // Provedení servisního volání Seznam widgetů = service.findAll (); // Uplatněte odpověď Assertions.assertEquals (2, widgets.size (), "findAll by měl vrátit 2 widgety"); } @Test @DisplayName ("Test save widget") void testSave () {// Nastavení našeho falešného úložiště Widget widget = nový Widget (1l, "Název widgetu", "Popis", 1); doReturn (widget) .when (repository) .save (any ()); // Provedení volání služby Widget returnedWidget = service.save (widget); // Uplatnit odpověď Assertions.assertNotNull (returnedWidget, "Uložený widget by neměl mít hodnotu null"); Assertions.assertEquals (2, returnedWidget.getVersion (), "Verze by měla být zvýšena"); }} 

The WidgetServiceTest třída je opatřena poznámkami s @SpringBootTest anotace, která skenuje CLASSPATH pro všechny Spring konfigurační třídy a fazole a nastaví kontext aplikace Spring pro testovací třídu. Všimněte si, že WidgetServiceTest implicitně zahrnuje také @ExtendWith (SpringExtension.class) anotace, prostřednictvím @SpringBootTest anotace, která integruje testovací třídu s JUnit 5.

Testovací třída také používá Spring's @Autowired anotace k autowire a WidgetService testovat proti a používá Mockito @MockBean anotace k vytvoření falešného WidgetRepository. V tuto chvíli máme faleš WidgetRepository které můžeme nakonfigurovat, a skutečné WidgetService s předstíranou WidgetRepository zapojený do toho.

Testování jarní služby

První zkušební metoda, testFindById (), vykonává WidgetServiceje findById () metoda, která by měla vrátit Volitelný který obsahuje a Widget. Začínáme vytvořením Widget že chceme WidgetRepository vrátit. Poté využijeme Mockito API ke konfiguraci WidgetRepository :: findById metoda. Struktura naší falešné logiky je následující:

 doReturn (VALUE_TO_RETURN). kdy (MOCK_CLASS_INSTANCE) .MOCK_METHOD 

V tomto případě říkáme: Vrátit Volitelný naší Widget když je úložiště findById () metoda je volána s argumentem 1 (jako a dlouho).

Dále vyvoláme WidgetServiceje findById metoda s argumentem 1. Poté ověříme, že je přítomen a že se vrátil Widget je ten, který jsme nakonfigurovali falešně WidgetRepository vrátit.

$config[zx-auto] not found$config[zx-overlay] not found