creating mocks spies mockito with code examples
Výukový program Mockito Spy and Mocks:
V tomto Séria výučbových programov Mockito , náš predchádzajúci návod nám dal Úvod do rámca Mockito . V tomto tutoriále sa naučíme koncept Mocks and Spies in Mockito.
Čo sú to vysmievatelia a špióni?
Mocks aj Spies sú typy testovacích dvojíc, ktoré sú užitočné pri písaní jednotkových testov.
Mocks sú úplnou náhradou za závislosť a je možné ich naprogramovať tak, aby vrátili zadaný výstup, kedykoľvek sa volá falošná metóda. Mockito poskytuje predvolenú implementáciu pre všetky falošné metódy.
Čo sa dozviete:
- Čo sú špióni?
- Vytváranie makiet
- Vytváranie špiónov
- Ako vložiť simulované závislosti pre testovanú triedu / objekt?
- tipy a triky
- Príklady kódu - Spies & Mocks
- Zdrojový kód
- Odporúčané čítanie
Čo sú špióni?
Špióni sú v podstate obalom skutočnej inštancie vysmievanej závislosti. To znamená, že vyžaduje novú inštanciu objektu alebo závislosti a potom nad ňu pridá obálku zosmiešňovaného objektu. Štandardne Spies volajú skutočné metódy Objektu, pokiaľ nie sú ukradnuté.
Špióni poskytujú určité ďalšie právomoci, napríklad to, aké argumenty boli dodané pri volaní metódy, či bola skutočná metóda vôbec volaná atď.
Stručne povedané, pre Spies:
- Vyžaduje sa skutočná inštancia objektu.
- Spies dáva flexibilitu na potlačenie niektorých (alebo všetkých) metód špehovaného objektu. V tom čase sa špión v podstate nazýva čiastočne alebo zosmiešňovaným alebo prepichnutým predmetom alebo sa naň odkazuje.
- Interakcie vyvolané špehovaným objektom je možné sledovať a overiť.
Všeobecne sa Spies nepoužívajú veľmi často, ale môžu byť užitočné pri testovaní starších aplikácií, pri ktorých sa závislosti nedajú úplne zosmiešniť.
Pri všetkom popise Mock and Spy máme na mysli fiktívnu triedu / objekt s názvom „DiscountCalculator“, ktorú chceme zosmiešniť / špehovať.
Má niekoľko metód, ako je uvedené nižšie:
vypočítaťzľavu - Vypočíta zľavnenú cenu daného produktu.
getDiscountLimit - Načíta horný limit zľavového limitu pre produkt.
Vytváranie makiet
# 1) Vysmievanie sa pomocou kódu
Mockito dáva niekoľko preťažených verzií Mockito. Vysmievaná metóda a umožňuje vytváranie falošných závislostí.
Syntax:
Mockito.mock(Class classToMock)
Príklad:
Predpokladajme, že názov triedy je DiscountCalculator, aby ste vytvorili falošný kód:
DiscountCalculator mockedDiscountCalculator = Mockito.mock(DiscountCalculator.class)
Je dôležité si uvedomiť, že Mock je možné vytvoriť pre rozhranie aj pre konkrétnu triedu.
Keď je objekt zosmiešňovaný, pokiaľ nie je zatknutý, všetky metódy predvolene vrátia hodnotu null .
DiscountCalculator mockDiscountCalculator = Mockito.mock(DiscountCalculator.class);
# 2) Vysmievanie sa s anotáciami
Namiesto zosmiešňovania pomocou statickej metódy „mock“ v knižnici Mockito poskytuje aj stenografický spôsob vytvárania zosmiešňovaní pomocou anotácie „@Mock“.
Najväčšou výhodou tohto prístupu je, že je jednoduchý a umožňuje kombinovať deklaráciu a v podstate inicializáciu. Testy sú tiež čitateľnejšie a nedochádza k opakovanej inicializácii falošných správ, keď sa rovnaký falošný test používa na viacerých miestach.
Aby sa zabezpečila inicializácia Mock prostredníctvom tohto prístupu, je potrebné, aby sme pre testovanú triedu nazvali „MockitoAnnotations.initMocks (this)“. Toto je ideálny kandidát na to, aby ste sa stali súčasťou metódy „beforeEach“ programu Junit, ktorá zaisťuje inicializáciu falošných správ vždy, keď sa z tejto triedy vykoná test.
Syntax:
@Mock private transient DiscountCalculator mockedDiscountCalculator;
Vytváranie špiónov
Podobne ako Mocks, aj Spies je možné vytvoriť dvoma spôsobmi:
# 1) Tvorba špiónov s kódom
Mockito.spy je statická metóda, ktorá sa používa na vytvorenie „špionážneho“ objektu / obalu okolo skutočnej inštancie objektu.
Syntax:
najlepší softvér na prevod videa pre Mac
private transient ItemService itemService = new ItemServiceImpl() private transient ItemService spiedItemService = Mockito.spy(itemService);
# 2) Tvorba špiónov s anotáciami
Podobne ako v prípade Mock, aj Spies je možné vytvoriť pomocou anotácie @Spy.
Rovnako pre inicializáciu Spy musíte zabezpečiť, aby sa MockitoAnnotations.initMocks (toto) volali skôr, ako sa Spy použije v skutočnom teste, aby sa špión inicializoval.
Syntax:
@Spy private transient ItemService spiedItemService = new ItemServiceImpl();
Ako vložiť simulované závislosti pre testovanú triedu / objekt?
Ak chceme vytvoriť falošný objekt testovanej triedy s ostatnými falošnými závislosťami, môžeme použiť anotáciu @InjectMocks.
Toto v podstate robí to, že všetky objekty označené anotáciami @Mock (alebo @Spy) sú vložené ako dodávateľ alebo injekcia vlastnosti do triedy Object a potom je možné overiť interakcie s konečným Mocked objektom.
Znova treba povedať, že @InjectMocks je skratka proti vytvoreniu nového Object triedy a poskytuje zosmiešňované objekty závislostí.
Poďme to pochopiť pomocou príkladu:
Predpokladajme, že existuje trieda PriceCalculator, ktorá má DiscountCalculator a UserService ako závislosti, ktoré sa vkladajú cez polia Constructor alebo Property.
Na vytvorenie implementácie Mocked pre triedu kalkulačky ceny teda môžeme použiť 2 prístupy:
# 1) Vytvorte novú inštanciu PriceCalculatora a vložiť Mocked závislosti
@Mock private transient DiscountCalculator mockedDiscountCalculator; @Mock private transient UserService userService; @Mock private transient ItemService mockedItemService; private transient PriceCalculator priceCalculator; @BeforeEach public void beforeEach() { MockitoAnnotations.initMocks(this); priceCalculator = new PriceCalculator(mockedDiscountCalculator, userService, mockedItemService); }
# 2) Vytvorte zosmiešnená inštancia PriceCalculatora a vloženie závislostí prostredníctvom anotácie @InjectMocks
@Mock private transient DiscountCalculator mockedDiscountCalculator; @Mock private transient UserService userService; @Mock private transient ItemService mockedItemService; @InjectMocks private transient PriceCalculator priceCalculator; @BeforeEach public void beforeEach() { MockitoAnnotations.initMocks(this);
Anotácia InjectMocks sa v skutočnosti pokúša vložiť zosmiešnené závislosti pomocou jedného z nasledujúcich prístupov:
- Vstrekovanie na základe konštruktéra - Využíva konštruktor pre testovanú triedu.
- Na základe nastavovacích metód - Ak tam nie je konštruktér, Mockito sa pokúša vstreknúť pomocou nástrojov na nastavenie vlastností.
- Field Based - Ak vyššie uvedené 2 nie sú k dispozícii, pokúsi sa priamo podať injekciu prostredníctvom polí.
tipy a triky
# 1) Nastavenie rôznych stubov pre rôzne volania rovnakou metódou:
Keď sa stubbed metóda volá viackrát vo vnútri testovanej metódy (alebo je stubbed metóda v slučke a chcete zakaždým vrátiť iný výstup), môžete nastaviť Mock tak, aby zakaždým vrátila inú stubbed odpoveď.
Napríklad: Predpokladajme, že chcete ItemService Ak chcete vrátiť inú položku pre 3 po sebe nasledujúce hovory a vo svojej metóde máte položky deklarované ako testy Item1, Item2 a Item3, môžete ich jednoducho vrátiť pre 3 po sebe nasledujúce vyvolania pomocou nasledujúceho kódu:
@Test public void calculatePrice_withCorrectInput_returnsValidResult() { // Arrange ItemSku item1 = new ItemSku(); ItemSku item2 = new ItemSku(); ItemSku item3 = new ItemSku(); // Setup Mocks when(mockedItemService.getItemDetails(anyInt())).thenReturn(item1, item2, item3); // Assert //TODO - add assert statements }
#dva) Vyhodenie výnimky cez Mock: Toto je veľmi častý scenár, keď chcete otestovať / overiť následný prúd / závislosť spôsobujúcu výnimku a skontrolovať chovanie testovaného systému. Aby ste však mohli vyhodiť výnimku od Mocka, budete musieť nastaviť stub pomocou thenThrow.
@Test public void calculatePrice_withInCorrectInput_throwsException() { // Arrange ItemSku item1 = new ItemSku(); // Setup Mocks when(mockedItemService.getItemDetails(anyInt())).thenThrow(new ItemServiceException(anyString())); // Assert //TODO - add assert statements }
Pri zápasoch ako anyInt () a anyString () sa nenechajte zastrašiť, pretože sa ich budeme venovať v nasledujúcich článkoch. Ale v zásade vám dávajú flexibilitu na poskytnutie ľubovoľných celočíselných a reťazcových hodnôt bez akýchkoľvek konkrétnych funkčných argumentov.
Príklady kódu - Spies & Mocks
Ako už bolo spomenuté, Spies aj Mocks sú typom testovacích dvojíc a majú svoje vlastné využitie.
Aj keď sú špióni užitoční na testovanie starších aplikácií (a kde nie sú možné falošné správy), pre všetky ostatné pekne napísané testovateľné metódy / triedy postačuje Mocks väčšine testovacích potrieb jednotky.
Rovnaký príklad: Napíšme test pomocou Mocks pre PriceCalculator -> metóda countPrice (Metóda počíta položku Cena menej o príslušné zľavy)
Trieda PriceCalculator a testovaná metóda CalculatePrice vyzerajú takto:
public class PriceCalculator { public DiscountCalculator discountCalculator; public UserService userService; public ItemService itemService; public PriceCalculator(DiscountCalculator discountCalculator, UserService userService, ItemService itemService) { this.discountCalculator = discountCalculator; this.userService = userService; this.itemService = itemService; } public double calculatePrice(int itemSkuCode, int customerAccountId) { double price = 0; // get Item details ItemSku sku = itemService.getItemDetails(itemSkuCode); // get User and calculate price CustomerProfile customerProfile = userService.getUser(customerAccountId); double basePrice = sku.getPrice(); price = basePrice - (basePrice* (sku.getApplicableDiscount() + customerProfile.getExtraLoyaltyDiscountPercentage())/100); return price; } }
Teraz si napíšme pozitívny test na túto metódu.
Chystáme sa zameniť službu userService a službu za položky, ako je uvedené nižšie:
- UserService vždy vráti CustomerProfile s loyaltyDiscountPercentage nastaveným na 2.
- Služba ItemService vždy vráti položku so základnou cenou 100 a platnou zľavou 5.
- Pri vyššie uvedených hodnotách vyjde očakávaná cena vrátená testovanou metódou na 93 $.
Tu je kód na test:
@Test public void calculatePrice_withCorrectInput_returnsExpectedPrice() { // Arrange ItemSku item1 = new ItemSku(); item1.setApplicableDiscount(5.00); item1.setPrice(100.00); CustomerProfile customerProfile = new CustomerProfile(); customerProfile.setExtraLoyaltyDiscountPercentage(2.00); double expectedPrice = 93.00; // Setting up stubbed responses using mocks when(mockedItemService.getItemDetails(anyInt())).thenReturn(item1); when(mockedUserService.getUser(anyInt())).thenReturn(customerProfile); // Act double actualPrice = priceCalculator.calculatePrice(123,5432); // Assert assertEquals(expectedPrice, actualPrice); }
Ako vidíte, vo vyššie uvedenom teste - tvrdíme, že skutočná cena vrátená metódou sa rovná očakávanej cene, tj. 93,00.
Poďme si teraz napísať test pomocou programu Spy.
Budeme špehovať ItemService a budeme kódovať implementáciu ItemService takým spôsobom, že vždy vráti položku so základnou cenou 200 a použiteľnou zľavou 10,00% (zvyšok falošného nastavenia zostáva rovnaký), kedykoľvek je volaná s skuCode 2367.
@InjectMocks private PriceCalculator priceCalculator; @Mock private DiscountCalculator mockedDiscountCalculator; @Mock private UserService mockedUserService; @Spy private ItemService mockedItemService = new ItemServiceImpl(); @BeforeEach public void beforeEach() { MockitoAnnotations.initMocks(this); } @Test public void calculatePrice_withCorrectInputRealMethodCall_returnsExpectedPrice() { // Arrange CustomerProfile customerProfile = new CustomerProfile(); customerProfile.setExtraLoyaltyDiscountPercentage(2.00); double expectedPrice = 176.00; // Setting up stubbed responses using mocks when(mockedUserService.getUser(anyInt())).thenReturn(customerProfile); // Act double actualPrice = priceCalculator.calculatePrice(2367,5432); // Assert assertEquals(expectedPrice, actualPrice);
Teraz sa pozrime Príklad výnimky vyvolanej ItemService, pretože dostupné množstvo položky bolo 0. Nastavíme falošnú akciu, ktorá vyvolá výnimku.
@InjectMocks private PriceCalculator priceCalculator; @Mock private DiscountCalculator mockedDiscountCalculator; @Mock private UserService mockedUserService; @Mock private ItemService mockedItemService = new ItemServiceImpl(); @BeforeEach public void beforeEach() { MockitoAnnotations.initMocks(this); } @Test public void calculatePrice_whenItemNotAvailable_throwsException() { // Arrange CustomerProfile customerProfile = new CustomerProfile(); customerProfile.setExtraLoyaltyDiscountPercentage(2.00); double expectedPrice = 176.00; // Setting up stubbed responses using mocks when(mockedUserService.getUser(anyInt())).thenReturn(customerProfile); when(mockedItemService.getItemDetails(anyInt())).thenThrow(new ItemServiceException(anyString())); // Act & Assert assertThrows(ItemServiceException.class, () -> priceCalculator.calculatePrice(123, 234)); }
S vyššie uvedenými príkladmi som sa pokúsil vysvetliť koncept Mocks & Spies a ako ich možno kombinovať tak, aby vytvorili efektívne a užitočné testy jednotky.
Môže existovať niekoľko kombinácií týchto techník, aby sa získala sada testov, ktoré zlepšia pokrytie testovanej metódy, čím sa zabezpečí veľká úroveň dôvery v kód a kód bude odolnejší voči regresným chybám.
Zdrojový kód
Rozhrania
DiscountCalculator
public interface DiscountCalculator { double calculateDiscount(ItemSku itemSku, double markedPrice); void calculateProfitability(ItemSku itemSku, CustomerProfile customerProfile); }
ItemService
public interface ItemService { ItemSku getItemDetails(int skuCode) throws ItemServiceException; }
UserService
public interface UserService { void addUser(CustomerProfile customerProfile); void deleteUser(CustomerProfile customerProfile); CustomerProfile getUser(int customerAccountId); }
Implementácie rozhraní
DiscountCalculatorImpl
public class DiscountCalculatorImpl implements DiscountCalculator { @Override public double calculateDiscount(ItemSku itemSku, double markedPrice) { return 0; } @Override public void calculateProfitability(ItemSku itemSku, CustomerProfile customerProfile) { } }
ItemServiceImpl
čo je bezpečnostný kľúč pre wifi
public class DiscountCalculatorImpl implements DiscountCalculator { @Override public double calculateDiscount(ItemSku itemSku, double markedPrice) { return 0; } @Override public void calculateProfitability(ItemSku itemSku, CustomerProfile customerProfile) { } }
Modely
Zákaznícky profil
public class CustomerProfile { private String customerName; private String loyaltyTier; private String customerAddress; private String accountId; private double extraLoyaltyDiscountPercentage; public double getExtraLoyaltyDiscountPercentage() { return extraLoyaltyDiscountPercentage; } public void setExtraLoyaltyDiscountPercentage(double extraLoyaltyDiscountPercentage) { this.extraLoyaltyDiscountPercentage = extraLoyaltyDiscountPercentage; } public String getAccountId() { return accountId; } public void setAccountId(String accountId) { this.accountId = accountId; } public String getCustomerName() { return customerName; } public void setCustomerName(String customerName) { this.customerName = customerName; } public String getLoyaltyTier() { return loyaltyTier; } public void setLoyaltyTier(String loyaltyTier) { this.loyaltyTier = loyaltyTier; } public String getCustomerAddress() { return customerAddress; } public void setCustomerAddress(String customerAddress) { this.customerAddress = customerAddress; } }
ItemSku
public class ItemSku { private int skuCode; private double price; private double maxDiscount; private double margin; private int totalQuantity; private double applicableDiscount; public double getApplicableDiscount() { return applicableDiscount; } public void setApplicableDiscount(double applicableDiscount) { this.applicableDiscount = applicableDiscount; } public int getTotalQuantity() { return totalQuantity; } public void setTotalQuantity(int totalQuantity) { this.totalQuantity = totalQuantity; } public int getSkuCode() { return skuCode; } public void setSkuCode(int skuCode) { this.skuCode = skuCode; } public double getPrice() { return price; } public void setPrice(double price) { this.price = price; } public double getMaxDiscount() { return maxDiscount; } public void setMaxDiscount(double maxDiscount) { this.maxDiscount = maxDiscount; } public double getMargin() { return margin; } public void setMargin(double margin) { this.margin = margin; } }
Testovaná trieda - PriceCalculator
public class PriceCalculator { public DiscountCalculator discountCalculator; public UserService userService; public ItemService itemService; public PriceCalculator(DiscountCalculator discountCalculator, UserService userService, ItemService itemService){ this.discountCalculator = discountCalculator; this.userService = userService; this.itemService = itemService; } public double calculatePrice(int itemSkuCode, int customerAccountId) { double price = 0; // get Item details ItemSku sku = itemService.getItemDetails(itemSkuCode); // get User and calculate price CustomerProfile customerProfile = userService.getUser(customerAccountId); double basePrice = sku.getPrice(); price = basePrice - (basePrice* (sku.getApplicableDiscount() + customerProfile.getExtraLoyaltyDiscountPercentage())/100); return price; } }
Testy jednotiek - PriceCalculatorUnitTests
public class PriceCalculatorUnitTests { @InjectMocks private PriceCalculator priceCalculator; @Mock private DiscountCalculator mockedDiscountCalculator; @Mock private UserService mockedUserService; @Mock private ItemService mockedItemService; @BeforeEach public void beforeEach() { MockitoAnnotations.initMocks(this); } @Test public void calculatePrice_withCorrectInput_returnsExpectedPrice() { // Arrange ItemSku item1 = new ItemSku(); item1.setApplicableDiscount(5.00); item1.setPrice(100.00); CustomerProfile customerProfile = new CustomerProfile(); customerProfile.setExtraLoyaltyDiscountPercentage(2.00); double expectedPrice = 93.00; // Setting up stubbed responses using mocks when(mockedItemService.getItemDetails(anyInt())).thenReturn(item1); when(mockedUserService.getUser(anyInt())).thenReturn(customerProfile); // Act double actualPrice = priceCalculator.calculatePrice(123,5432); // Assert assertEquals(expectedPrice, actualPrice); } @Test @Disabled // to enable this change the ItemService MOCK to SPY public void calculatePrice_withCorrectInputRealMethodCall_returnsExpectedPrice() { // Arrange CustomerProfile customerProfile = new CustomerProfile(); customerProfile.setExtraLoyaltyDiscountPercentage(2.00); double expectedPrice = 176.00; // Setting up stubbed responses using mocks when(mockedUserService.getUser(anyInt())).thenReturn(customerProfile); // Act double actualPrice = priceCalculator.calculatePrice(2367,5432); // Assert assertEquals(expectedPrice, actualPrice); } @Test public void calculatePrice_whenItemNotAvailable_throwsException() { // Arrange CustomerProfile customerProfile = new CustomerProfile(); customerProfile.setExtraLoyaltyDiscountPercentage(2.00); double expectedPrice = 176.00; // Setting up stubbed responses using mocks when(mockedUserService.getUser(anyInt())).thenReturn(customerProfile); when(mockedItemService.getItemDetails(anyInt())).thenThrow(new ItemServiceException(anyString())); // Act & Assert assertThrows(ItemServiceException.class, () -> priceCalculator.calculatePrice(123, 234)); } }
Rôzne typy porovnávačov, ktoré poskytuje Mockito, sú vysvetlené v našom pripravovanom výučbe.
Výukový program PREV | NEXT Tutorial
Odporúčané čítanie
- Rôzne typy zápaliek, ktoré poskytuje Mockito
- Výukový program Mockito: Rámec Mockito pre simuláciu pri testovaní jednotiek
- Vytváranie epochálnych testov pomocou epoch Studio pre Eclipse
- Výukový program pre Python DateTime s príkladmi
- Vystrihnite príkaz v systéme Unix s príkladmi
- Syntax príkazov Unix Cat, možnosti s príkladmi
- Používanie kurzora v MongoDB s príkladmi
- Príkaz Ls v systéme Unix s príkladmi