Programavimas

Pasityčiojimai ir suklupimai - bandymų dvigubumo supratimas su Mockito

Dažnas dalykas, su kuriuo susiduriu, yra tai, kad komandos, naudojančios pašaipų sistemą, daro prielaidą, kad tyčiojasi.

Jie nežino, kad pasityčiojimai yra tik vienas iš „Test Doubles“, kurį Gerardas Meszarosas priskyrė kategorijai xunitpatterns.com.

Svarbu suvokti, kad kiekvieno tipo dvigubas bandymas turi skirtingą vaidmenį atliekant bandymus. Lygiai taip pat, kaip jums reikia išmokti skirtingų modelių ar pertvarkyti, turite suprasti primityvius kiekvieno tipo bandymų vaidmenis. Tada juos galima sujungti, kad atitiktų jūsų testavimo poreikius.

Apžvelgsiu labai trumpą istoriją, kaip atsirado ši klasifikacija ir kuo skiriasi visi tipai.

Aš tai atliksiu naudodamas trumpus, paprastus pavyzdžius, pateiktus „Mockito“.

Daugelį metų žmonės rašė lengvąsias sistemos komponentų versijas, kad padėtų atlikti bandymus. Apskritai tai buvo vadinama užsispyrimu. 2000 m. Straipsnyje „Endo-Testing: Unit Testing with Mock Objects“ pristatyta maketo objekto sąvoka. Nuo to laiko stubai, pasityčiojimai ir daugybė kitų tipų bandomųjų objektų Meszaros priskiriami bandomiesiems dvejetiniams.

Į šią terminologiją atkreipė dėmesį Martinas Fowleris knygoje „Mock are not Stubs“ ir ji yra perimama „Microsoft“ bendruomenėje, kaip parodyta „Test Doubles tęstinumo tyrinėjimas“.

Nuoroda į kiekvieną iš šių svarbių straipsnių pateikiama nuorodų skyriuje.

Aukščiau pateiktoje diagramoje parodyti dažniausiai naudojami dvigubo bandymo tipai. Šiame URL pateikiama gera kryžminė nuoroda į kiekvieną modelį ir jų ypatybes bei alternatyvią terminologiją.

//xunitpatterns.com/Test%20Double.html

„Mockito“ yra bandomasis šnipo pagrindas, kurio išmokti yra labai paprasta. Pažymėtina, kad „Mockito“ yra tai, kad prieš bandymą nėra apibrėžti bet kokių tyčinių objektų lūkesčiai, kaip jie kartais būna kitose tyčinėse sistemose. Tai veda prie natūralesnio stiliaus (IMHO) pradedant tyčiotis.

Šie pavyzdžiai yra skirti tik tam, kad būtų galima lengvai parodyti, kaip „Mockito“ naudojama įvairių tipų bandymų dviguboms versijoms įgyvendinti.

Yra daug daugiau konkrečių pavyzdžių, kaip svetainėje naudoti „Mockito“.

//docs.mockito.googlecode.com/hg/latest/org/mockito/Mockito.html

Toliau pateikiami keli pagrindiniai pavyzdžiai, naudojant Mockito, siekiant parodyti kiekvieno bandymo dvigubo vaidmens vaidmenį, kaip apibrėžė Meszaros.

Kiekvienam pateikiau nuorodą į pagrindinį apibrėžimą, kad galėtumėte gauti daugiau pavyzdžių ir išsamų apibrėžimą.

//xunitpatterns.com/Dummy%20Object.html

Tai yra paprasčiausias iš visų bandomųjų dublių. Tai objektas, kurio neįdiegta, jis naudojamas tik metodo iškvietimų argumentams, kurie nėra svarbūs jūsų testui, užpildyti.

Pavyzdžiui, žemiau pateiktame kode klientui sukurti naudojamas daug kodo, kuris nėra svarbus bandymui.

Testas negalėjo mažiau rūpintis, kuris klientas yra pridėtas, jei tik klientų skaičius grįš vienas.

viešasis klientas createDummyCustomer () {County county = new County ("Essex"); Miesto miestas = naujas miestas („Romford“, apskritis); Adreso adresas = naujas adresas ("1234 Bank Street", miestas); Kliento klientas = naujas klientas („john“, „dobie“, adresas); grįžęs klientas; } @Test public void addCustomerTest () {Customer dummy = createDummyCustomer (); AddressBook addressBook = nauja adresų knyga (); addressBook.addCustomer (manekenas); assertEquals (1, addressBook.getNumberOfCustomers ()); } 

Mums iš tikrųjų nerūpi kliento objekto turinys, tačiau jis yra būtinas. Galime išbandyti nulinę vertę, tačiau jei kodas teisingas, galite tikėtis, kad bus padaryta tam tikra išimtis.

@Test (tikėtinas = Exception.class) public void addNullCustomerTest () {Kliento manekenas = null; AddressBook addressBook = nauja adresų knyga (); addressBook.addCustomer (manekenas); } 

Norėdami to išvengti, norėdami elgtis galime naudoti paprastą „Mockito“ manekeną.

@Test public void addCustomerWithDummyTest () {Customer dummy = mock (Customer.class); AddressBook addressBook = nauja adresų knyga (); addressBook.addCustomer (manekenas); „Assert.assertEquals“ (1, addressBook.getNumberOfCustomers ()); } 

Šis paprastas kodas sukuria manekeno objektą, kuris bus perduotas skambučiui.

Kliento manekenas = pasityčiojimas (Customer.class);

Neapsigaukite dėl pasityčiojimo sintaksės - čia vaidinamas ne manekenas, o manekenas.

Tai išskiria bandomojo dvigubo vaidmens vaidmuo, o ne sintaksė, naudojama kuriant.

Ši klasė veikia kaip paprastas klientų klasės pakaitalas, todėl testą labai lengva perskaityti.

//xunitpatterns.com/Test%20Stub.html

Bandomojo vaidmens vaidmuo yra grąžinti kontroliuojamas vertes į bandomą objektą. Tai apibūdinama kaip netiesioginė įvestis į testą. Tikimės, kad pavyzdys paaiškins, ką tai reiškia.

Paimkite šį kodą

viešoji klasė „SimplePricingService“ įgyvendina „PricingService“ {PricingRepository saugyklą; public SimplePricingService (PricingRepository pricingRepository) {this.repository = pricingRepository; } @Paisyti viešą kainos kainą prekyba (prekybos prekyba) {grąžinimo saugykla.getPriceForTrade (prekyba); } @Paisyti viešą kainą getTotalPriceForTrades (kolekcijos sandoriai) {Price totalPrice = nauja kaina (); for (Prekybos prekyba: prekyba) {Price tradePrice = repository.getPriceForTrade (prekyba); totalPrice = totalPrice.add (prekybos kaina); } return totalPrice; } 

„SimplePricingService“ turi vieną bendradarbiaujantį objektą - sandorių saugyklą. Sandorių duomenų saugykla kainų tarnybai pateikia prekybos kainas naudodama „getPriceForTrade“ metodą.

Kad galėtume išbandyti verslininkų logiką „SimplePricingService“, turime kontroliuoti šias netiesiogines įvestis

t., įvestis, kurios mes niekada neišlaikėme.

Tai parodyta žemiau.

Šiame pavyzdyje mes priverčiame „PricingRepository“ grąžinti žinomas reikšmes, kurias galima naudoti norint patikrinti „SimpleTradeService“ verslo logiką.

@Test public void testGetHighestPricedTrade () meta išimtį {Price price1 = new Price (10); Kaina kaina2 = nauja Kaina (15); Kainos kaina3 = nauja Kaina (25); PricingRepository pricingRepository = pasityčiojimas (PricingRepository.class); kai (pricingRepository.getPriceForTrade (bet koks (Trade.class))) .thenReturn (kaina1, kaina2, kaina3); „PricingService“ paslauga = nauja „SimplePricingService“ (pricingRepository); Kaina aukščiausiaKaina = service.getHighestPricedTrade (getTrades ()); „assertEquals“ (kaina3.getAmount (), didžiausiaPrice.getAmount ()); } 

Saboteur pavyzdys

Yra 2 įprasti bandomųjų stuburų variantai: „Responder’s“ ir „Saboteur“.

Atsakiklis naudojamas išbandyti laimingą kelią, kaip ir ankstesniame pavyzdyje.

Išbandant išskirtinį elgesį, kaip nurodyta toliau, naudojamas sabotažas.

@Test (numatytas = TradeNotFoundException.class) public void testInvalidTrade () meta išimtį {Trade trade = new FixtureHelper (). GetTrade (); TradeRepository tradeRepository = pasityčiojimas (TradeRepository.class); kai (tradeRepository.getTradeById (anyLong ())) .thenThrow (nauja TradeNotFoundException ()); TradingService tradingService = nauja „SimpleTradingService“ („tradeRepository“); tradingService.getTradeById (prekyba.getId ()); } 

//xunitpatterns.com/Mock%20Object.html

Piktdariniai objektai naudojami objekto elgesiui patikrinti bandymo metu. Turėdamas omenyje objekto elgesį, turiu omenyje, kad vykdant testą objekte atliekami teisingi metodai ir keliai.

Tai labai skiriasi nuo papildomo vaidmens, kuris naudojamas norint gauti rezultatus bet kokiam bandomam veiksmui.

Šaknyje mes naudojame metodo grąžinimo vertės apibrėžimo modelį.

kada (klientas.getSurname ()). tadaGrįžti (pavardė); 

Plėtroje mes patikriname objekto elgesį naudodami šią formą.

patikrinti (listMock) .add (s); 

Čia yra paprastas pavyzdys, kai norime patikrinti, ar naujas sandoris yra teisingai audituotas.

Čia yra pagrindinis kodas.

viešoji klasė „SimpleTradingService“ įgyvendina „TradingService“ {TradeRepository tradeRepository; AuditService auditService; public SimpleTradingService (TradeRepository tradeRepository, AuditService auditService) {this.tradeRepository = tradeRepository; this.auditService = audito tarnyba; } public Ilgas createTrade (prekyba prekyba) meta CreateTradeException {Long id = tradeRepository.createTrade (prekyba); auditService.logNewTrade (prekyba); grąžinimo ID; } 

Toliau pateiktas testas sukuria sandorio duomenų saugyklą ir „AuditService“ pašaipą

Tada mes kviečiame tikrinti pasityčiotą „AuditService“, kad įsitikintume, jog „TradeService“ tai vadina

„logNewTrade“ metodas teisingai

@Mock TradeRepository tradeRepository; @Mock AuditService auditService; @Test public void testAuditLogEntryMadeForNewTrade () išmeta išimtį {Trade trade = new Trade ("Ref 1", "Description 1"); kada (tradeRepository.createTrade (prekyba)). thenReturn (anyLong ()); TradingService tradingService = naujas „SimpleTradingService“ („tradeRepository“, „auditService“); tradingService.createTrade (prekyba); patikrinti (auditService) .logNewTrade (prekyba); } 

Šioje eilutėje tikrinamas pajuokiamas „AuditService“.

patikrinti (auditService) .logNewTrade (prekyba);

Šis testas leidžia mums parodyti, kad audito tarnyba, kurdama prekybą, elgiasi teisingai.

//xunitpatterns.com/Test%20Spy.html

Verta pažvelgti į aukščiau pateiktą nuorodą, kurioje pateikiamas griežtas bandomojo šnipo apibrėžimas.

Tačiau „Mockito“ mėgstu jį naudoti, kad galėčiau suvynioti tikrą objektą ir tada patikrinti ar modifikuoti jo elgesį, kad būtų lengviau atlikti bandymus.

Čia yra pavyzdys, kai mes tikrinome standartinį sąrašo elgesį. Atkreipkite dėmesį, kad mes galime patikrinti, ar yra iškviestas pridėjimo metodas, ir taip pat teigti, kad elementas buvo įtrauktas į sąrašą.

@Spy sąrašas listSpy = new ArrayList (); @Test public void testSpyReturnsRealValues ​​() meta išimtį {String s = "dobie"; listSpy.add (nauja (-os) eilutė (-ės)); patikrinti (listSpy) .add (s); „assertEquals“ (1, listSpy.size ()); } 

Palyginkite tai su imituojančiu objektu, kur galima patvirtinti tik metodo iškvietimą. Kadangi mes tyčiojamės tik iš sąrašo elgesio, jis neužfiksuoja, kad elementas buvo pridėtas, ir grąžina numatytąją nulio vertę, kai iškviečiame metodo size ().

@Mock List listMock = new ArrayList (); @Test public void testMockReturnsZero () išmeta išimtį {String s = "dobie"; listMock.add (nauja (-os) eilutė (-ės)); patikrinti (listMock) .add (s); assertEquals (0, listMock.size ()); } 

Dar viena naudinga „testSpy“ funkcija yra galimybė sugadinti skambučius. Kai tai bus padaryta, objektas elgsis taip, kaip įprasta, kol bus iškviestas „stubled“ metodas.

Šiame pavyzdyje mes gaudome „get“ metodą, kad visada būtų „RuntimeException“. Likęs elgesys išlieka tas pats.

@Test (numatytas = RuntimeException.class) public void testSpyReturnsStubbedValues ​​() meta išimtį {listSpy.add (nauja eilutė ("dobie")); „assertEquals“ (1, listSpy.size ()); kai (listSpy.get (anyInt ())). thenThrow (nauja „RuntimeException“); listSpy.get (0); } 

Šiame pavyzdyje mes vėl išlaikome pagrindinę elgseną, bet pakeičiame size () metodą, kad grąžintume 1 iš pradžių ir 5 už visus tolesnius skambučius.

public void testSpyReturnsStubbedValues2 () meta išimtį {int size = 5; kada (listSpy.size ()). tadaGrįžti (1, dydis); int mockedListSize = listSpy.size (); assertEquals (1, mockedListSize); mockedListSize = listSpy.size (); assertEquals (5, mockedListSize); mockedListSize = listSpy.size (); assertEquals (5, mockedListSize); } 

Tai yra gana magija!

//xunitpatterns.com/Fake%20Object.html

Netikri daiktai paprastai yra rankų darbo arba lengvi daiktai, naudojami tik bandymams ir netinka gamybai. Geras pavyzdys būtų atminties duomenų bazė arba padirbtas paslaugų sluoksnis.

Jie paprastai teikia daug daugiau funkcionalumo nei standartiniai bandymai dvigubai ir todėl tikriausiai paprastai nėra kandidatai įgyvendinti naudojant „Mockito“. Tai nereiškia, kad jų nebuvo galima sukonstruoti kaip tokių, tiesiog tikriausiai neverta taip įgyvendinti.

Išbandykite dvigubus modelius

„Endo-Testing“: vieneto testavimas naudojant „Mock Objects“

Pasityčiojimo vaidmenys, o ne objektai

Pašaipos nėra stubai

//msdn.microsoft.com/en-us/magazine/cc163358.aspx

Šią istoriją „Mock and Stubs - Understanding Test Doubles With Mockito“ iš pradžių išleido „JavaWorld“.