Ohjelmointi

Pilkkaa ja tynkä - ymmärtää koe kaksinpeli kanssa Mockito

Yleinen asia, jonka kohtaan, on se, että pilkkaavaa kehystä käyttävät joukkueet olettavat pilkkaavansa.

He eivät ole tietoisia siitä, että pilkat ovat vain yksi monista 'Test Doubles' -tuotteista, jotka Gerard Meszaros on luokitellut osoitteessa xunitpatterns.com.

On tärkeää ymmärtää, että jokaisella testin kaksoistyypillä on erilainen rooli testauksessa. Samalla tavalla kuin sinun täytyy oppia erilaisia ​​malleja tai refaktorointia, sinun on ymmärrettävä kunkin testityypin primitiiviset roolit. Nämä voidaan sitten yhdistää testaustarpeidesi saavuttamiseksi.

Käsittelen hyvin lyhyen historian siitä, miten tämä luokitus syntyi ja miten kukin tyypistä eroaa.

Teen tämän käyttämällä lyhyitä, yksinkertaisia ​​esimerkkejä Mockitossa.

Vuosien ajan ihmiset ovat kirjoittaneet järjestelmän komponenttien kevyitä versioita testauksen helpottamiseksi. Yleensä sitä kutsuttiin tynkäykseksi. Vuonna 2000 artikkelissa Endo-Testing: Unit Testing with Mock Objects esiteltiin Mock Object -konsepti. Siitä lähtien Meszaros on luokitellut Stubs, Mocks ja useita muita testikohteita Test Doublesiksi.

Tähän terminologiaan on viitannut Martin Fowler julkaisussa "Mocks Ar’t Stubs", ja se on otettu käyttöön Microsoft-yhteisössä, kuten "Testin kaksinpelin jatkamisen tutkiminen" osoittaa.

Linkki näihin tärkeisiin artikkeleihin on esitetty viiteosassa.

Yllä oleva kaavio näyttää yleisesti käytetyt kaksoistestityypit. Seuraava URL antaa hyvän viittauksen kaikkiin malleihin ja niiden ominaisuuksiin sekä vaihtoehtoiseen terminologiaan.

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

Mockito on testi-vakoojakehys, ja se on helppo oppia. Mockito-sovelluksessa on huomattava, että odotuksia mistä tahansa objektiista ei ole määritelty ennen testiä, koska ne joskus ovat muissa pilkkaavissa puitteissa. Tämä johtaa luonnollisempaan tyyliin (IMHO), kun alkaa pilkata.

Seuraavat esimerkit ovat tässä puhtaasti esittelemällä yksinkertainen esimerkki Mockiton käytöstä erityyppisten testinäkymien toteuttamiseksi.

Verkkosivustolla on paljon suurempi määrä erityisiä esimerkkejä Mockiton käytöstä.

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

Seuraavassa on joitain perusesimerkkejä Mockiton avulla kunkin testin kaksoistehtävän osoittamiseksi Meszaroksen määrittelemän mukaisesti.

Olen sisällyttänyt linkin kunkin päämäärittelyyn, jotta saat lisää esimerkkejä ja täydellisen määritelmän.

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

Tämä on yksinkertaisin kaikista testinäkymistä. Tämä on objekti, jota ei ole toteutettu ja jota käytetään pelkästään metodikutsujen argumenttien täyttämiseen, joilla ei ole merkitystä testisi kannalta.

Esimerkiksi alla oleva koodi käyttää paljon koodia asiakkaan luomiseen, mikä ei ole tärkeä testin kannalta.

Testi ei voinut välittää vähemmän siitä, mikä asiakas lisätään, kunhan asiakasmäärä palaa yhtenä.

julkinen asiakas createDummyCustomer () {County county = new County ("Essex"); Kaupungin kaupunki = uusi kaupunki ("Romford", lääni); Osoiteosoite = uusi osoite ("1234 Bank Street", kaupunki); Asiakasasiakas = uusi asiakas ("john", "dobie", osoite); paluuasiakas; } @Test public void addCustomerTest () {Customer dummy = createDummyCustomer (); AddressBook addressBook = uusi osoitekirja (); addressBook.addCustomer (tutti); assertEquals (1, addressBook.getNumberOfCustomers ()); } 

Emme todellakaan välitä asiakasobjektin sisällöstä - mutta sitä vaaditaan. Voimme kokeilla nolla-arvoa, mutta jos koodi on oikea, voit odottaa jonkinlaista poikkeusta.

@Test (odotettavissa = Exception.class) public void addNullCustomerTest () {Customer dummy = null; AddressBook addressBook = uusi osoitekirja (); addressBook.addCustomer (tutti); } 

Tämän välttämiseksi voimme käyttää yksinkertaista Mockito-nuken halutun käyttäytymisen saamiseksi.

@Test public void addCustomerWithDummyTest () {Customer dummy = pilkata (Customer.class); AddressBook addressBook = uusi osoitekirja (); addressBook.addCustomer (tutti); Assert.assertEquals (1, addressBook.getNumberOfCustomers ()); } 

Tämä yksinkertainen koodi luo näennäiskohteen, joka välitetään puheluun.

Asiakkaan nukke = pilkata (Asiakasluokka);

Älä hämää pilkkasyntaksi - tässä pelattava rooli on nuken, ei pilkun.

Se erottaa testitubelin roolin, ei sen syntaksissa, jota käytetään sen luomiseen.

Tämä luokka toimii yksinkertaisena asiakasluokan korvikkeena ja tekee testistä erittäin helppolukuisen.

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

Testityhmän tehtävänä on palauttaa hallitut arvot testattavalle objektille. Nämä kuvataan epäsuorina syötteinä testiin. Toivottavasti esimerkki selventää, mitä tämä tarkoittaa.

Ota seuraava koodi

public class SimplePricingService toteuttaa PricingService {PricingRepository-arkisto; public SimplePricingService (PricingRepository pricingRepository) {this.repository = pricingRepository; } @Override public Price priceTrade (kauppakauppa) {return repository.getPriceForTrade (kauppa); } @Override public Price getTotalPriceForTrades (Collection trades) {Price totalPrice = new Price (); for (Kauppakauppa: kaupat) {HintakauppaHinta = arkisto.getPriceForTrade (kauppa); totalPrice = totalPrice.add (kauppahinta); } return totalPrice; } 

SimplePricingServicessa on yksi yhteistyöobjekti, joka on kauppatietovarasto. Kauppatietorekisteri tarjoaa kauppahinnat hinnoittelupalvelulle getPriceForTrade-menetelmän avulla.

Jotta voimme testata businees-logiikkaa SimplePricingServicessa, meidän on hallittava näitä epäsuoria syötteitä

ts. syötteet, joita emme koskaan läpäisseet testiin.

Tämä näkyy alla.

Seuraavassa esimerkissä vaaditaan PricingRepository palauttamaan tunnetut arvot, joita voidaan käyttää SimpleTradeServicen liiketoimintalogiikan testaamiseen.

@Test public void testGetHighestPricedTrade () heittää poikkeuksen {Hinta hinta1 = uusi Hinta (10); Hintahinta2 = uusi hinta (15); Hintahinta3 = uusi hinta (25); PricingRepository pricingRepository = pilkata (PricingRepository.class); milloin (pricingRepository.getPriceForTrade (mikä tahansa (Trade.class))) .thenReturn (hinta1, hinta2, hinta3); PricingService-palvelu = uusi SimplePricingService (pricingRepository); Hinta korkein hinta = service.getHighestPricedTrade (getTrades ()); assertEquals (hinta3.getAmount (), korkeinHinta.getAmount ()); } 

Saboteur-esimerkki

Testityypistä on 2 yleistä muunnosta: Responder's ja Saboteur's.

Vastaajaa käytetään onnellisen tien testaamiseen kuten edellisessä esimerkissä.

Saboteuria käytetään poikkeuksellisen käyttäytymisen testaamiseen alla esitetyllä tavalla.

@Test (odotettavissa = TradeNotFoundException.class) public void testInvalidTrade () heittää poikkeuksen {Kauppakauppa = uusi FixtureHelper (). GetTrade (); TradeRepository tradeRepository = pilkata (TradeRepository.class); milloin (tradeRepository.getTradeById (anyLong ())) .thenThrow (uusi TradeNotFoundException ()); TradingService tradingService = uusi SimpleTradingService (tradeRepository); tradingService.getTradeById (kauppa.getId ()); } 

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

Mock-objekteja käytetään objektin käyttäytymisen tarkistamiseen testin aikana. Kohteen käyttäytymisellä tarkoitan, että tarkistamme, että objektilla suoritetaan oikeat menetelmät ja polut, kun testi suoritetaan.

Tämä eroaa suuresti tynkän tukiroolista, jota käytetään tulosten tuottamiseen kaikelle mitä testaat.

Tynkissä käytämme mallia, jolla määritetään palautusarvo menetelmälle.

milloin (asiakas.GetSurname ()). thenReturn (sukunimi); 

Tarkastuksessa tarkistamme kohteen käyttäytymisen seuraavalla lomakkeella.

tarkista (listMock) .add (s); 

Tässä on yksinkertainen esimerkki, jossa haluamme testata, että uusi kauppa tarkastetaan oikein.

Tässä on pääkoodi.

public class SimpleTradingService toteuttaa TradingService {TradeRepository tradeRepository; AuditService auditService; public SimpleTradingService (TradeRepository tradeRepository, AuditService auditService) {this.tradeRepository = tradeRepository; this.auditService = auditService; } public Pitkä createTrade (kauppakauppa) heittää CreateTradeException {Long id = tradeRepository.createTrade (kauppa); auditService.logNewTrade (kauppa); palautustunnus; } 

Alla oleva testi luo kaupan tietovarastolle ja pilkkaa AuditServicea

Sitten kutsumme vahvistuksen pilkatulle AuditServicelle varmistaaksemme, että TradeService kutsuu sitä

logNewTrade-menetelmä oikein

@Mock TradeRepository tradeRepository; @Mock AuditService auditService; @Test public void testAuditLogEntryMadeForNewTrade () heittää poikkeuksen {kauppakauppa = uusi kauppa ("Ref 1", "Description 1"); milloin (tradeRepository.createTrade (trade)). thenReturn (anyLong ()); TradingService tradingService = uusi SimpleTradingService (tradeRepository, auditService); tradingService.createTrade (kauppa); tarkista (auditService) .logNewTrade (kauppa); } 

Seuraava rivi tarkistaa pilkatun AuditServicen.

tarkista (auditService) .logNewTrade (kauppa);

Tämän testin avulla voimme osoittaa, että tilintarkastuspalvelu toimii oikein kauppaa luodessamme.

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

Kannattaa tarkastella yllä olevaa linkkiä testivakoojan tiukka määritelmä.

Haluan kuitenkin käyttää sitä Mockitossa sen avulla, että voit kääriä todellisen objektin ja sitten tarkistaa tai muokata sen käyttäytymistä tukemaan testaustasi.

Tässä on esimerkki, kun tarkistimme luettelon vakiokäyttäytymisen. Huomaa, että voimme molemmat tarkistaa, että lisäystapa kutsutaan, ja myös väittää, että kohde on lisätty luetteloon.

@Spy List listSpy = new ArrayList (); @Test public void testSpyReturnsRealValues ​​() heittää poikkeuksen {String s = "dobie"; listSpy.add (uudet merkkijonot); tarkista (listSpy) .add (s); assertEquals (1, listSpy.size ()); } 

Vertaa tätä mock-objektin käyttämiseen, jossa vain menetelmäpuhelu voidaan vahvistaa. Koska pilkkaamme vain luettelon käyttäytymistä, se ei kirjaa, että kohde on lisätty, ja palauttaa oletusarvon nolla, kun kutsumme size () -menetelmää.

@Mock List listMock = new ArrayList (); @Test public void testMockReturnsZero () heittää poikkeuksen {String s = "dobie"; listMock.add (uudet merkkijonot); tarkista (listMock) .add (s); assertEquals (0, listMock.size ()); } 

Toinen hyödyllinen ominaisuus testSpy: ssä on kyky piilottaa puhelut. Kun tämä on tehty, objekti käyttäytyy normaalisti, kunnes jumppamenetelmä kutsutaan.

Tässä esimerkissä haetaan get-menetelmä aina heittämään RuntimeException. Muu käyttäytyminen pysyy samana.

@Test (odotettavissa = RuntimeException.class) public void testSpyReturnsStubbedValues ​​() heittää poikkeuksen {listSpy.add (uusi merkkijono ("dobie")); assertEquals (1, listSpy.size ()); milloin (listSpy.get (anyInt ())). thenThrow (uusi RuntimeException ()); listSpy.get (0); } 

Tässä esimerkissä säilytetään taas ydinkäyttäytyminen, mutta muutetaan size () -menetelmää palauttamaan 1 aluksi ja 5 kaikille seuraaville puheluille.

public void testSpyReturnsStubbedValues2 () heittää poikkeuksen {int size = 5; milloin (listSpy.size ()). thenReturn (1, koko); int mockedListSize = listSpy.size (); assertEquals (1, mockedListSize); mockedListSize = listSpy.size (); assertEquals (5, mockedListSize); mockedListSize = listSpy.size (); assertEquals (5, mockedListSize); } 

Tämä on aika taikaa!

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

Väärennetyt esineet ovat yleensä käsityönä valmistettuja tai kevyitä esineitä, joita käytetään vain testaamiseen ja jotka eivät sovellu tuotantoon. Hyvä esimerkki olisi muistin sisäinen tietokanta tai väärennetty palvelutaso.

Niillä on taipumus tarjota paljon enemmän toiminnallisuutta kuin tavallisissa testikaksoisissa, eivätkä sellaiset todennäköisesti ole ehdokkaita Mockitoa käyttävälle toteutukselle. Tämä ei tarkoita sitä, että niitä ei voitaisi sellaisenaan rakentaa, vaan että sitä ei todennäköisesti kannata toteuttaa tällä tavalla.

Testaa kaksoiskuviot

Endo-testaus: Yksikkötestaus mock-objekteilla

Pilkata rooleja, ei esineitä

Pilkut eivät ole tynkä

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

Tämän tarinan "Mocks and Stubs - Understanding Test Doubles With Mockito" julkaisi alun perin JavaWorld.

$config[zx-auto] not found$config[zx-overlay] not found