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.

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á WidgetService
je 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 WidgetService
je 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.