creating mocks spies mockito with code examples
Vodič za Mockito Spy and Mocks:
U ovome Serija udžbenika Mockito , naš prethodni tutorial dao nam je Uvod u Mockito Framework . U ovom uputstvu naučit ćemo koncept rugalica i špijuna u Mockitu.
Što su rugalice i špijuni?
I rugalice i špijuni vrste su testnih parova koji pomažu u pisanju cjelovitih testova.
Ismijavanja su potpuna zamjena za ovisnost i mogu se programirati za vraćanje navedenog izlaza kad god se pozove metoda na lažnom prikazu. Mockito nudi zadanu implementaciju za sve metode izrugivanja.
Što ćete naučiti:
- Što su špijuni?
- Stvaranje ismijavanja
- Stvaranje špijuna
- Kako ubrizgati izmijenjene ovisnosti za klasu / objekt koji se testira?
- savjeti i trikovi
- Primjeri koda - špijuni i izrugivanje
- Izvorni kod
- Preporučena literatura
Što su špijuni?
Špijuni su u osnovi omot stvarne instance izrugivane ovisnosti. To znači da zahtijeva novu instancu Objekta ili ovisnosti, a zatim dodaje omot ismijanog objekta preko njega. Prema zadanim postavkama, Špijuni pozivaju stvarne metode Objekta, osim ako nisu oštećene.
Špijuni pružaju određene dodatne moći poput onih koji su argumenti dostavljeni pozivu metode, je li uopće bila zvana prava metoda itd.
Ukratko, za špijune:
- Potrebna je stvarna instanca objekta.
- Špijuni daju fleksibilnost da ubode neke (ili sve) metode špijuniranog objekta. U to vrijeme špijun se u biti naziva ili upućuje na djelomično izrugivani ili izbodeni predmet.
- Interakcije pozvane na špijuniranom objektu mogu se pratiti radi provjere.
Općenito, špijuni se ne koriste često, ali mogu biti korisni za jedinično testiranje naslijeđenih aplikacija u kojima se ovisnosti ne mogu u potpunosti ismijati.
Za sav opis izrugivanja i špijuna, pozivamo se na fiktivnu klasu / objekt nazvan ‘DiscountCalculator’ koji želimo ismijati / špijunirati.
Ima nekoliko metoda kao što je prikazano u nastavku:
izračunajPopust - Izračunava cijenu s popustom određenog proizvoda.
getDiscountLimit - Dohvaća gornju granicu popusta za proizvod.
Stvaranje ismijavanja
# 1) Lažno stvaranje pomoću koda
Mockito daje nekoliko preopterećenih verzija Mockita. Mocks metoda i omogućuje stvaranje lažnih slika za ovisnosti.
Sintaksa:
Mockito.mock(Class classToMock)Primjer:
Pretpostavimo da je naziv klase DiscountCalculator, da bismo stvorili lažni kod:
DiscountCalculator mockedDiscountCalculator = Mockito.mock(DiscountCalculator.class)Važno je napomenuti da se Mock može stvoriti za sučelje ili za konkretnu klasu.
Kada se objekt ismijava, osim ako se sve metode po zadanom ne vrate null .
DiscountCalculator mockDiscountCalculator = Mockito.mock(DiscountCalculator.class);# 2) Lažno stvaranje s napomenama
Umjesto ruganja pomoću statičke metode 'mock' Mockito biblioteke, on također nudi kratki način stvaranja lažnih referenci pomoću bilješke '@Mock'.
Najveća prednost ovog pristupa je u tome što je jednostavan i omogućava kombiniranje deklaracije i u osnovi inicijalizacije. Također čini testove čitljivijima i izbjegava ponovljenu inicijalizaciju lažnih djela kada se isti lažni obrazac koristi na nekoliko mjesta.
Da bismo osigurali Mock inicijalizaciju putem ovog pristupa, zahtijevalo je da pozovemo 'MockitoAnnotations.initMocks (this)' za klasu koja se ispituje. Ovo je idealan kandidat da bude dio metode 'beforeEach' Junita koja osigurava da se lažni prikazi pokreću svaki put kada se iz te klase izvrši test.
Sintaksa:
@Mock private transient DiscountCalculator mockedDiscountCalculator;Stvaranje špijuna
Slično izrugivanju, i špijuni se mogu stvoriti na 2 načina:
# 1) Stvaranje špijuna pomoću koda
Mockito.spy je statična metoda koja se koristi za stvaranje 'špijunskog' objekta / omota oko stvarne instance objekta.
Sintaksa:
Pitanja za intervju temeljena na scenariju pl sql
private transient ItemService itemService = new ItemServiceImpl() private transient ItemService spiedItemService = Mockito.spy(itemService);# 2) Stvaranje špijuna s Bilješkama
Slično Mocku, i špijuni se mogu stvoriti pomoću @Spy bilješke.
I za inicijalizaciju špijuna morate osigurati da se MockitoAnnotations.initMocks (ovo) pozove prije nego što se špijun koristi u stvarnom testu kako bi se špijun inicijalizirao.
Sintaksa:
@Spy private transient ItemService spiedItemService = new ItemServiceImpl();Kako ubrizgati izmijenjene ovisnosti za klasu / objekt koji se testira?
Kada želimo stvoriti lažni objekt klase koja se testira s ostalim ismijanim ovisnostima, možemo koristiti bilješku @InjectMocks.
Ono što u osnovi čini jest da se svi objekti označeni oznakama @Mock (ili @Spy) ubrizgavaju kao Izvođač ili ubrizgavanje svojstva u klasu Objekt, a zatim se interakcije mogu provjeriti na konačnom Mocked objektu.
Opet, nepotrebno je spominjati, @InjectMocks je stenografija protiv stvaranja novog Objekta klase i nudi izrugane objekte ovisnosti.
Razumijemo to na primjeru:
Pretpostavimo da postoji klasa PriceCalculator, koja ima DiscountCalculator i UserService kao ovisnosti koje se ubacuju putem polja Constructor ili Property.
Dakle, da bismo stvorili Mocked implementaciju za klasu Kalkulator cijena, možemo koristiti 2 pristupa:
# 1) Stvori nova instanca PriceCalculator-a i ubrizgajte Mocked ovisnosti
@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) Stvori ismijani primjerak PriceCalculatora i ubrizgavanje ovisnosti putem bilješke @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); Bilješka InjectMocks zapravo pokušava ubrizgati ismijane ovisnosti koristeći jedan od dolje navedenih pristupa:
- Injektiranje na temelju konstruktora - Koristi konstruktor za klasu koja se ispituje.
- Na temelju metoda postavljača - Kada konstruktora nema, Mockito pokušava ubrizgati pomoću postavljača svojstava.
- Na terenu - Kada gornja 2 nisu dostupna, tada se izravno pokušava ubrizgati putem polja.
savjeti i trikovi
# 1) Postavljanje različitih kvara za različite pozive iste metode:
Kada se metoda s ispitivanjem više puta pozove unutar metode koja se ispituje (ili je metoda s metodom koja je u petlji i želite svaki put vratiti različite izlaze), tada možete postaviti Mock da svaki put vraća različite odgovore s promjenom.
Na primjer: Pretpostavimo da želite ItemService da biste vratili drugu stavku za 3 uzastopna poziva, a stavke su u vašoj metodi deklarirane u testovima kao stavke 1, 2 i 3, a zatim ih možete jednostavno vratiti za 3 uzastopna poziva pomoću donjeg koda:
@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) Bacanje iznimke kroz podsmijeh: Ovo je vrlo čest scenarij kada želite testirati / provjeriti nizvodno / ovisnost bacajući iznimku i provjeriti ponašanje testiranog sustava. Međutim, da biste izbacili iznimku od strane Mock-a, morat ćete postaviti klizač koristeći 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 }Za utakmice poput anyInt () i anyString (), nemojte se zastrašivati jer će oni biti pokriveni u sljedećim člancima. Ali u osnovi, oni vam samo daju fleksibilnost da navedete bilo koju vrijednost Integer i String, bez ikakvih specifičnih argumenata funkcije.
Primjeri koda - špijuni i izrugivanje
Kao što je ranije rečeno, i Špijuni i Mockovi su vrsta test parova i imaju svoje običaje.
Iako su špijuni korisni za testiranje naslijeđenih aplikacija (i tamo gdje izrugivanja nisu moguća), za sve ostale lijepo napisane provjerljive metode / klase, ismijavanja su dovoljne za većinu potreba Jedinstvenog testiranja.
Za isti primjer: Napišimo test koristeći Mocks for PriceCalculator -> izračunajPrice metoda (Metoda izračunava stavkuCijena manja od primjenjivih popusta)
Klasa PriceCalculator i metoda koja se testira izračunajuCijena izgleda kao što je prikazano u nastavku:
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; } }Napišimo sada pozitivan test za ovu metodu.
Upotrijebit ćemo userService i uslugu artikala kako je spomenuto u nastavku:
- UserService uvijek će vratiti CustomerProfile s loyaltyDiscountPercentage postavljenim na 2.
- ItemService uvijek će vratiti predmet s osnovnom cijenom 100 i primjenjivim popustom od 5.
- S gornjim vrijednostima, očekivanaCijena vraćena ispitnom metodom iznosi 93 $.
Evo koda za 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); } Kao što vidite, u gornjem testu - Tvrdimo da je stvarnaCijena vraćena metodom jednaka očekivanojCijeni, tj. 93,00.
najbolja aplikacija zaslona zaslona za Windows 10
Napišimo sada test koristeći Spy.
Špijunirat ćemo ItemService i kodirati ćemo izvedbu ItemService na način da uvijek vrati stavku s basePrice 200 i primjenjivim Popust od 10,00% (ostatak lažne postavke ostaje isti) kad god se pozove s skuCode od 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); Sada, da vidimo Primjer iznimke koju dobacuje ItemService jer je raspoloživa količina predmeta bila 0. Postavit ćemo lažno izbacivanje iznimke.
@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 gornjim primjerima pokušao sam objasniti koncept Mocks & Spies i kako se oni mogu kombinirati kako bi se stvorili učinkoviti i korisni Unit testovi.
Može biti više kombinacija ovih tehnika kako bi se dobio niz testova koji povećavaju pokrivenost ispitne metode, osiguravajući time veliku razinu povjerenja u kôd i čineći kôd otpornijim na greške u regresiji.
Izvorni kod
Sučelja
Kalkulator popusta
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; }Korisnička usluga
public interface UserService { void addUser(CustomerProfile customerProfile); void deleteUser(CustomerProfile customerProfile); CustomerProfile getUser(int customerAccountId); }Implementacije sučelja
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
public class DiscountCalculatorImpl implements DiscountCalculator { @Override public double calculateDiscount(ItemSku itemSku, double markedPrice) { return 0; } @Override public void calculateProfitability(ItemSku itemSku, CustomerProfile customerProfile) { } }Modeli
Profil kupca
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; } }Predmet koji se ispituje - Kalkulator cijena
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; } } Jedinični testovi - 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)); } }Različite vrste podudaranja koje nudi Mockito objašnjene su u našem nadolazećem vodiču.
Preporučena literatura
- Različite vrste podudaranja koje pruža Mockito
- Vodič za Mockito: Mockito okvir za ruganje u jedinstvenom testiranju
- Stvaranje testova epoha pomoću epochs Studio za Eclipse
- Python DateTime Vodič s primjerima
- Izreži naredbu u Unixu s primjerima
- Sintaksa naredbe Unix Cat, mogućnosti s primjerima
- Primjena pokazivača u MongoDB-u s primjerima
- Ls naredba u Unixu s primjerima