creating mocks spies mockito with code examples
Водич за Моцкито Спи анд Моцкс:
У ово Моцкито Туториал серија , наш претходни водич нам је дао Увод у Моцкито Фрамеворк . У овом упутству ћемо научити концепт ругалица и шпијуна у Моцкиту.
Шта су ругалице и шпијуни?
И ругалице и шпијуни су типови пробних двојника који помажу у писању јединствених тестова.
Изругивања су потпуна замена за зависност и могу се програмирати за враћање наведеног излаза кад год се позове метода на лажном приказу. Моцкито нуди подразумевану имплементацију за све методе лажног представљања.
Шта ћете научити:
- Шта су шпијуни?
- Цреатинг Моцкс
- Стварање шпијуна
- Како убризгати исмеване зависности за класу / предмет који се тестира?
- савети и Трикови
- Примери кода - шпијуни и изругивање
- Изворни код
- Препоручено читање
Шта су шпијуни?
Шпијуни су у основи омот стварне инстанце исмејане зависности. То значи да захтева нову инстанцу објекта или зависности, а затим додаје омот исмеваног објекта преко ње. Подразумевано, Шпијуни позивају стварне методе Објекта, осим ако нису оштећене.
Шпијуни пружају одређене додатне моћи, попут аргумената који су достављени позиву методе, да ли је стварна метода уопште позвана итд.
Укратко, за шпијуне:
- Потребна је стварна инстанца објекта.
- Шпијуни дају флексибилност да убоде неке (или све) методе шпијунираног објекта. У то време, шпијун се у суштини назива или упућује на делимично изругиван или изударан предмет.
- Интеракције позване на шпијунираном објекту могу се пратити ради верификације.
Генерално, шпијуни се не користе често, али могу бити корисни за јединствено тестирање застарелих апликација где се зависности не могу у потпуности исмевати.
За сав опис изругивања и шпијуна, позивамо се на фиктивну класу / објекат под називом „ДисцоунтЦалцулатор“ који желимо да исмевамо / шпијунирамо.
Има неколико метода као што је приказано у наставку:
израчунај попуст - Израчунава снижену цену датог производа.
гетДисцоунтЛимит - Дохваћа горњу границу попуста за производ.
Цреатинг Моцкс
# 1) Лажно стварање помоћу кода
Моцкито даје неколико преоптерећених верзија Моцкито-а. Моцкс метода и омогућава креирање лажних за зависности.
Синтакса:
Mockito.mock(Class classToMock)
Пример:
Претпоставимо да је име класе ДисцоунтЦалцулатор, да бисмо креирали лажни код:
DiscountCalculator mockedDiscountCalculator = Mockito.mock(DiscountCalculator.class)
Важно је напоменути да Моцк може бити креиран за интерфејс или за конкретну класу.
Када се објекат исмева, осим ако се све методе по подразумеваном подешавању не врате нулл .
DiscountCalculator mockDiscountCalculator = Mockito.mock(DiscountCalculator.class);
# 2) Лажно стварање помоћу напомена
Уместо да се ругате помоћу статичке методе „моцк“ из Моцкито библиотеке, она такође нуди скраћени начин креирања форме помоћу анотације „@Моцк“.
Највећа предност овог приступа је што је једноставан и омогућава комбиновање декларације и суштински иницијализације. Такође чини тестове читљивијим и избегава поновну иницијализацију лажних дела када се исти лажни образац користи на више места.
Да бисмо осигурали Моцк иницијализацију кроз овај приступ, било је потребно да позовемо „МоцкитоАннотатионс.инитМоцкс (тхис)“ за класу која се тестира. Ово је идеалан кандидат да буде део методе 'бефореЕацх' Јунит-а која осигурава да се лажни прикази покрећу сваки пут када се из те класе изврши тест.
Синтакса:
@Mock private transient DiscountCalculator mockedDiscountCalculator;
Стварање шпијуна
Слично магији, шпијуни се такође могу креирати на 2 начина:
# 1) Стварање шпијуна помоћу кода
Моцкито.спи је статична метода која се користи за креирање ‘шпијунског’ објекта / омота око стварне инстанце објекта.
Синтакса:
тестирајте веб локацију на различитим прегледачима на мрежи
private transient ItemService itemService = new ItemServiceImpl() private transient ItemService spiedItemService = Mockito.spy(itemService);
# 2) Стварање шпијуна са напоменама
Слично Моцк-у, Шпијуни се могу креирати помоћу @Спи напомене.
И за иницијализацију шпијуна морате осигурати да се МоцкитоАннотатионс.инитМоцкс (ово) позове пре него што се шпијун користи у стварном тесту како би шпијун био иницијализован.
Синтакса:
@Spy private transient ItemService spiedItemService = new ItemServiceImpl();
Како убризгати исмеване зависности за класу / предмет који се тестира?
Када желимо да направимо лажни објекат класе која се тестира са осталим исмеваним зависностима, можемо да користимо @ИњецтМоцкс напомену.
Ово што у суштини ради је да се сви објекти означени са @Моцк (или @Спи) напоменама убризгају као Извођач или убризгавање својства у класу Објект, а затим се интеракције могу верификовати на коначном Моцкед објекту.
Опет, непотребно је напомињати, @ИњецтМоцкс је стенографија против стварања новог Објекта класе и нуди изругиване објекте зависности.
Разумимо ово на примеру:
Претпоставимо да постоји класа ПрицеЦалцулатор, која има ДисцоунтЦалцулатор и УсерСервице као зависности које се убризгавају преко поља Цонструцтор или Проперти.
Дакле, да бисмо креирали Моцкед имплементацију за класу Калкулатор цена, можемо користити 2 приступа:
# 1) Направи нова инстанца ПрицеЦалцулатор-а и убризгајте Моцкед зависности
@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) Направи подругљива инстанца ПрицеЦалцулатор-а и убризгавања зависности путем @ИњецтМоцкс напомене
@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);
ИњецтМоцкс напомена заправо покушава да убризгава изругиване зависности користећи један од доле наведених приступа:
- Ињекција заснована на конструктору - Користи конструктор за класу која се тестира.
- Сеттер Метходс Басед - Када конструктора нема, Моцкито покушава да убризга помоћу постављача својстава.
- На основу терена - Када горња 2 нису доступна, онда се директно покушава убризгати путем поља.
савети и Трикови
# 1) Постављање различитих клица за различите позиве исте методе:
Када се стуббед метода позива више пута у оквиру методе која се испитује (или је стуббед метода у петљи и желите сваки пут да вратите различите излазе), тада можете да подесите Моцк да сваки пут враћа различите табулиране одговоре.
На пример: Претпоставимо да желите ИтемСервице да бисте вратили другу ставку за 3 узастопна позива, а ставке су у вашој методи декларисане у тестовима као ставке 1, 2 и 3, а затим их можете једноставно вратити за 3 узастопна позивања помоћу доњег кода:
@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 }
#два) Бацање изузетка кроз подсмех: Ово је врло чест сценарио када желите да тестирате / верификујете низводно / зависност бацајући изузетак и проверите понашање система који се тестира. Међутим, да бисте избацили изузетак Моцк-ом, мораћете да подесите клатно користећи тхенТхров.
@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 }
За подударања попут аниИнт () и аниСтринг (), немојте се застрашивати јер ће они бити покривени у наредним чланцима. Али у основи, они вам само дају флексибилност да наведете било коју вредност Интегер и Стринг, без икаквих специфичних аргумената функције.
Примери кода - шпијуни и изругивање
Као што је раније речено, и Шпијуни и Изругивачи су врста тест парова и имају своју употребу.
Иако су шпијуни корисни за тестирање застарелих апликација (и тамо где исмеви нису могући), за све остале лепо написане провериве методе / класе, Мацкови задовољавају већину потреба за тестирање јединице.
За исти пример: Напишимо тест користећи Моцкс фор ПрицеЦалцулатор -> израчунајПрице метод (Метод израчунава итемПрице мање од применљивих попуста)
Класа ПрицеЦалцулатор и метода која се тестира израчунавајуЦену изгледа као што је приказано доле:
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; } }
Напишимо сада позитиван тест за ову методу.
Укинућемо усерСервице и услугу артикала како је поменуто у наставку:
- УсерСервице ће увек вратити ЦустомерПрофиле са лоиалтиДисцоунтПерцентаге постављеним на 2.
- ИтемСервице ће увек вратити предмет са основномЦена 100 и применљивим попустом 5.
- Са горе наведеним вредностима, очекиванаЦена враћена тест методом износи 93 $.
Ево кода за тест:
@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); }
Као што видите, у горенаведеном тесту - тврдимо да је стварнаЦена враћена методом једнака очекиванојЦена, односно 93,00.
Хајде сада да напишемо тест користећи Спи.
Шпијунираћемо ИтемСервице и кодираћемо ИтемСервице имплементацију на начин да увек врати ставку са басеПрице 200 и применљивим Попуст од 10,00% (остатак лажне поставке остаје исти) кад год се позове са скуЦоде од 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);
Сада, да видимо Пример изузетка који добацује ИтемСервице јер је расположива количина предмета износила 0. Поставићемо лажни испис за избацивање изузетка.
@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)); }
Са горњим примерима покушао сам да објасним концепт ругалица и шпијуна и како се они могу комбиновати да би се створили ефикасни и корисни Унит тестови.
Може бити више комбинација ових техника да би се добио низ тестова који побољшавају покривеност методе која се испитује, чиме се осигурава велики ниво поверења у код и чини код отпорнијим на грешке у регресији.
Изворни код
Интерфејси
ДисцоунтЦалцулатор
public interface DiscountCalculator { double calculateDiscount(ItemSku itemSku, double markedPrice); void calculateProfitability(ItemSku itemSku, CustomerProfile customerProfile); }
ИтемСервице
public interface ItemService { ItemSku getItemDetails(int skuCode) throws ItemServiceException; }
УсерСервице
public interface UserService { void addUser(CustomerProfile customerProfile); void deleteUser(CustomerProfile customerProfile); CustomerProfile getUser(int customerAccountId); }
Имплементације интерфејса
ДисцоунтЦалцулаторИмпл
public class DiscountCalculatorImpl implements DiscountCalculator { @Override public double calculateDiscount(ItemSku itemSku, double markedPrice) { return 0; } @Override public void calculateProfitability(ItemSku itemSku, CustomerProfile customerProfile) { } }
ИтемСервицеИмпл
public class DiscountCalculatorImpl implements DiscountCalculator { @Override public double calculateDiscount(ItemSku itemSku, double markedPrice) { return 0; } @Override public void calculateProfitability(ItemSku itemSku, CustomerProfile customerProfile) { } }
Модели
Профил купаца
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; } }
ИтемСку
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; } }
Предмет на тесту - Калкулатор цена
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; } }
Јединствени тестови - ПрицеЦалцулаторУнитТестс
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)); } }
Различите врсте подударања које пружа Моцкито објашњене су у нашем предстојећем упутству.
како отворити бин датотеке на андроид-у
ПРЕВ Туториал |. | СЛЕДЕЋА Лекција
Препоручено читање
- Различите врсте подударања које пружа Моцкито
- Водич за Моцкито: Моцкито оквир за ругање у јединственом тестирању
- Креирање тестова епоха помоћу епоцхс Студио за Ецлипсе
- Питхон ДатеТиме Водич са примерима
- Изрежи команду у Унику са примерима
- Синтакса наредбе Уник Цат, опције са примерима
- Употреба курсора у МонгоДБ са примерима
- Лс наредба у Унику са примерима