Mockito versus EasyMock versus JMockit

1. Inleiding

1.1. Overzicht

In dit bericht gaan we het hebben over spottend: wat het is, waarom het te gebruiken en verschillende voorbeelden van hoe je dezelfde testcase kunt bespotten met behulp van enkele van de meest gebruikte mocking-bibliotheken voor Java.

We beginnen met enkele formele / semi-formele definities van spottende concepten; dan presenteren we de te testen casus, volgen we voorbeelden voor elke bibliotheek en komen we tot enkele conclusies. De gekozen bibliotheken zijn Mockito, EasyMock en JMockit.

Als je het gevoel hebt dat je de basisprincipes van spotten al kent, kun je misschien naar punt 2 gaan zonder de volgende drie punten te lezen.

1.2. Redenen om Mocks te gebruiken

We gaan ervan uit dat u al codeert volgens een of andere gedreven ontwikkelingsmethodologie die is gericht op tests (TDD, ATDD of BDD). Of gewoon dat u een test wilt maken voor een bestaande klasse die afhankelijk is van afhankelijkheden om de functionaliteit te bereiken.

In ieder geval, als we een klasse testen, willen we dat test alleen de functionaliteit en niet die van de afhankelijkheden (ofwel omdat we hun implementatie vertrouwen of omdat we het zelf zullen testen).

Om dit te bereiken, moeten we het te testen object een vervanging bieden die we voor die afhankelijkheid kunnen controleren. Op deze manier kunnen we extreme retourwaarden afdwingen, uitzonderingen werpen of eenvoudig tijdrovende methoden terugbrengen tot een vaste retourwaarde.

Deze gecontroleerde vervanging is de bespotten, en het zal u helpen om testcodering te vereenvoudigen en testuitvoeringstijd te verkorten.

1.3. Mock-concepten en definities

Laten we eens kijken naar vier definities uit een artikel geschreven door Martin Fowler waarin de basisprincipes worden samengevat die iedereen over spot zou moeten weten:

  • Dummy objecten worden rondgeleid maar nooit daadwerkelijk gebruikt. Meestal worden ze alleen gebruikt om parameterlijsten te vullen.
  • Nep objecten hebben werkende implementaties, maar nemen meestal een snelkoppeling waardoor ze niet geschikt zijn voor productie (een in-memory database is een goed voorbeeld).
  • Stompjes geef standaardantwoorden op oproepen die tijdens de test zijn gemaakt, en reageer meestal helemaal niet op iets buiten wat voor de test is geprogrammeerd. Stubs kunnen ook informatie over oproepen registreren, zoals een e-mailgateway-stub die de berichten onthoudt die het ‘verzonden 'heeft, of misschien alleen hoeveel berichten het‘ verzonden' heeft.
  • Bespot zijn waar we het hier over hebben: objecten voorgeprogrammeerd met verwachtingen die een specificatie vormen van de oproepen die ze naar verwachting zullen ontvangen.

1.4 Bespotten of niet bespotten: dat is de vraag

Niet alles moet worden bespot. Soms is het beter om een ​​integratietest te doen, omdat het bespotten van die methode / functie gewoon zou werken voor weinig echt voordeel. In ons testgeval (dat wordt in het volgende punt getoond) zou dat het testen van de Login Dao.

De Login Dao zou een bibliotheek van derden gebruiken voor DB-toegang, en het bespotten ervan zou alleen bestaan ​​uit het verzekeren dat parameters waren voorbereid voor de oproep, maar we zouden nog steeds moeten testen of de oproep de gegevens retourneert die we wilden.

Om die reden zal het niet in dit voorbeeld worden opgenomen (hoewel we zowel de unit-test zouden kunnen schrijven met nepoproepen voor de externe bibliotheekoproepen EN een integratietest met DBUnit om de feitelijke prestaties van de externe bibliotheek te testen).

2. Testgeval

Laten we, met alles in het vorige gedeelte in gedachten, een vrij typische testcase voorstellen en hoe we deze zullen testen met spotjes (als het zinvol is om schijnwerpers te gebruiken). Dit zal ons helpen om een ​​gemeenschappelijk scenario te hebben om later de verschillende spotbibliotheken te kunnen vergelijken.

2.1 Voorgestelde zaak

De voorgestelde testcase is het inlogproces in een applicatie met een gelaagde architectuur.

Het aanmeldingsverzoek wordt afgehandeld door een controller die een service gebruikt die een DAO gebruikt (die zoekt naar gebruikersreferenties op een database). We zullen niet teveel ingaan op de implementatie van elke laag en zullen ons meer concentreren op het interacties tussen de componenten van elke laag.

Op deze manier hebben we een LoginController, een LoginService en een Inloggen DAO. Laten we ter verduidelijking een diagram bekijken:

2.2 Implementatie

We zullen nu de implementatie volgen die voor de testcase is gebruikt, zodat we kunnen begrijpen wat er gebeurt (of wat er zou moeten gebeuren) tijdens de tests.

We beginnen met het model dat voor alle bewerkingen wordt gebruikt, UserForm, die alleen de gebruikersnaam en het wachtwoord bevat (we gebruiken modifiers voor openbare toegang om dit te vereenvoudigen) en een getter-methode voor de gebruikersnaam veld om bespotten voor die eigenschap toe te staan:

openbare klasse UserForm {openbaar String-wachtwoord; openbare String gebruikersnaam; public String getUsername () {terugkeer gebruikersnaam; }}

Laten we verder gaan met Inloggen DAO, dat zal geen functionaliteit meer hebben, omdat we alleen willen dat de methoden er zijn, zodat we ze kunnen bespotten wanneer dat nodig is:

openbare klasse Loginão {openbare int login (UserForm userForm) {return 0; }}

Login Dao wordt gebruikt door LoginService in zijn Log in methode. LoginService zal ook een setCurrentUser methode die terugkeert leegte om die spot te testen.

openbare klasse LoginService {privé Login. private String currentUser; openbare booleaanse login (UserForm userForm) {assert null! = userForm; int loginResults = loginão.login (userForm); switch (loginResults) {case 1: return true; default: return false; }} public void setCurrentUser (String gebruikersnaam) {if (null! = gebruikersnaam) {this.currentUser = gebruikersnaam; }}}

Tenslotte, LoginController zal gebruiken LoginService voor zijn Log in methode. Dit omvat:

  • een geval waarin geen oproepen naar de bespotte dienst zullen worden gedaan.
  • een geval waarin slechts één methode wordt aangeroepen.
  • een geval waarin alle methoden worden aangeroepen.
  • een geval waarin het werpen van uitzonderingen wordt getest.
openbare klasse LoginController {openbare LoginService loginService; openbare String login (UserForm userForm) {if (null == userForm) {return "ERROR"; } else {boolean ingelogd; probeer {ingelogd = loginService.login (userForm); } catch (uitzondering e) {return "ERROR"; } if (ingelogd) {loginService.setCurrentUser (userForm.getUsername ()); terug "OK"; } else {retourneer "KO"; }}}}

Nu we hebben gezien wat we proberen te testen, laten we eens kijken hoe we het met elke bibliotheek bespotten.

3. Testopstelling

3.1 Mockito

Voor Mockito gebruiken we versie 2.8.9.

De eenvoudigste manier om mock-ups te maken en te gebruiken, is via de @Bespotten en @InjectMocks annotaties. De eerste zal een mock maken voor de klasse die wordt gebruikt om het veld te definiëren en de tweede zal proberen de gemaakte mock-ups in de geannoteerde mock te injecteren.

Er zijn meer annotaties zoals @Spion waarmee je een gedeeltelijke mock kunt maken (een mock die de normale implementatie gebruikt in niet-bespotte methoden).

Dat gezegd hebbende, je moet bellen MockitoAnnotations.initMocks (dit) alvorens tests uit te voeren die genoemde spotten zouden gebruiken om al deze "magie" te laten werken. Dit gebeurt meestal in een @Voordat geannoteerde methode. U kunt ook de MockitoJUnitRunner.

openbare klasse LoginControllerTest {@Mock privé Login. @Spy @InjectMocks privé LoginService spiedLoginService; @Mock privé LoginService loginService; @InjectMocks privé LoginController loginController; @Before public void setUp () {loginController = nieuwe LoginController (); MockitoAnnotations.initMocks (dit); }}

3.2 EasyMock

Voor EasyMock gebruiken we versie 3.4 (Javadoc). Houd er rekening mee dat u met EasyMock moet bellen om bespottingen te laten "werken" EasyMock.replay (mock) op elke testmethode, of u krijgt een uitzondering.

Mocks en geteste klassen kunnen ook worden gedefinieerd via annotaties, maar in dit geval zullen we in plaats van een statische methode aan te roepen, de EasyMockRunner voor de testklas.

Mocks worden gemaakt met de @Bespotten annotatie en het geteste object met de @Proef persoon one (die zijn afhankelijkheden geïnjecteerd krijgt vanuit gemaakte schijnwerpers). Het geteste object moet in-line worden gemaakt.

@RunWith (EasyMockRunner.class) openbare klasse LoginControllerTest {@Mock privé Login. @Mock privé LoginService loginService; @TestSubject private LoginController loginController = nieuwe LoginController (); }

3.3. JMockit

Voor JMockit gebruiken we versie 1.24 (Javadoc), aangezien versie 1.25 nog niet is uitgebracht (althans tijdens het schrijven hiervan).

De installatie voor JMockit is net zo eenvoudig als met Mockito, met de uitzondering dat er geen specifieke annotatie is voor gedeeltelijke bespotten (en ook echt niet nodig) en die u moet gebruiken JMockit als testagent.

Mocks worden gedefinieerd met behulp van de @Injecteerbaar annotatie (waardoor er slechts één nepinstantie wordt gemaakt) of met @Mocked annotatie (dat zal spotjes maken voor elke instantie van de klasse van het geannoteerde veld).

Het geteste exemplaar wordt gemaakt (en de bespotte afhankelijkheden worden geïnjecteerd) met behulp van de @Getest annotatie.

@RunWith (JMockit.class) openbare klasse LoginControllerTest {@Injectable privé Login. @Injectable privé LoginService loginService; @Getest privé LoginController loginController; }

4. Controleren of er geen oproepen zijn om te bespotten

4.1. Mockito

Om te controleren of een mock-up geen oproepen heeft ontvangen in Mockito, hebt u de methode verifieerZeroInteractions () die een schijnvertoning accepteert.

@Test openbare void assertThatNoMethodHasBeenCalled () {loginController.login (null); Mockito.verifyZeroInteractions (loginService); }

4.2. EasyMock

Om te verifiëren dat een mock-up geen oproepen heeft ontvangen, specificeert u eenvoudig geen gedrag, speelt u de mock opnieuw af en tenslotte verifieert u het.

@Test openbare void assertThatNoMethodHasBeenCalled () {EasyMock.replay (loginService); loginController.login (null); EasyMock.verify (loginService); }

4.3. JMockit

Om te verifiëren dat een mock-up geen oproepen heeft ontvangen, specificeert u eenvoudigweg geen verwachtingen voor die mock en voert u een FullVerifications (mock) voor die spot.

@Test openbare void assertThatNoMethodHasBeenCalled () {loginController.login (null); nieuwe FullVerifications (loginService) {}; }

5. Mocked Method-oproepen definiëren en oproepen naar Mocks verifiëren

5.1. Mockito

Voor bespotten methode oproepen, je kunt gebruiken Mockito.when (mock.method (args)). ThenReturn (waarde). Hier kunt u verschillende waarden retourneren voor meer dan één aanroep, door ze gewoon toe te voegen als meer parameters: thenReturn (waarde1, waarde2, waarde-n, ...).

Merk op dat je met deze syntaxis geen ongeldige retourmethoden kunt bespotten. In genoemde gevallen gebruikt u een verificatie van genoemde methode (zoals weergegeven op regel 11).

Voor oproepen verifiëren tot een mock die je kunt gebruiken Mockito.verify (mock) .method (args) en u kunt ook controleren of er geen oproepen meer zijn gedaan naar een nep met verifiërenNoMoreInteractions (mock).

Voor het verifiëren van argumentenkunt u specifieke waarden doorgeven of vooraf gedefinieerde matchers gebruiken, zoals ieder(), anyString (), anyInt (). Er zijn veel meer van dat soort matchers en zelfs de mogelijkheid om je matchers te definiëren, die we in de volgende voorbeelden zullen zien.

@Test openbare void assertTwoMethodsHaveBeenCalled () {UserForm userForm = nieuw UserForm (); userForm.username = "foo"; Mockito.when (loginService.login (userForm)). ThenReturn (true); String login = loginController.login (userForm); Assert.assertEquals ("OK", login); Mockito.verify (loginService) .login (userForm); Mockito.verify (loginService) .setCurrentUser ("foo"); } @Test openbare void assertOnlyOneMethodHasBeenCalled () {UserForm userForm = nieuw UserForm (); userForm.username = "foo"; Mockito.when (loginService.login (userForm)). ThenReturn (false); String login = loginController.login (userForm); Assert.assertEquals ("KO", login); Mockito.verify (loginService) .login (userForm); Mockito.verifyNoMoreInteractions (loginService); }

5.2. EasyMock

Voor bespotten methode oproepen, je gebruikt EasyMock.expect (mock.method (args)). AndReturn (waarde).

Voor oproepen verifiëren tot een mock, je kunt gebruiken EasyMock.verify (mock), maar je moet het bellen altijd na roeping EasyMock.replay (mock).

Voor het verifiëren van argumentenkun je specifieke waarden doorgeven, of je hebt vooraf gedefinieerde matchers zoals isA(Klasse. Klas), anyString (), anyInt (), en nog veel meer van dat soort matchers en opnieuw de mogelijkheid om je matchers te definiëren.

@Test openbare void assertTwoMethodsHaveBeenCalled () {UserForm userForm = nieuw UserForm (); userForm.username = "foo"; EasyMock.expect (loginService.login (userForm)). AndReturn (true); loginService.setCurrentUser ("foo"); EasyMock.replay (loginService); String login = loginController.login (userForm); Assert.assertEquals ("OK", login); EasyMock.verify (loginService); } @Test openbare void assertOnlyOneMethodHasBeenCalled () {UserForm userForm = nieuw UserForm (); userForm.username = "foo"; EasyMock.expect (loginService.login (userForm)). AndReturn (false); EasyMock.replay (loginService); String login = loginController.login (userForm); Assert.assertEquals ("KO", login); EasyMock.verify (loginService); }

5.3. JMockit

Met JMockit heb je stappen gedefinieerd om te testen: opnemen, opnieuw afspelen en verifiëren.

Vermelding wordt gedaan in een nieuw Verwachtingen () {{}} blok (waarin u acties kunt definiëren voor meerdere spotjes), herhaling wordt gedaan door simpelweg een methode van de geteste klasse aan te roepen (die een bespot object zou moeten aanroepen), en verificatie wordt gedaan in een nieuw Verificaties () {{}} blok (waarin u verificaties voor verschillende mock-ups kunt definiëren).

Voor spottende methode aanroepen, je kunt gebruiken mock.method (args); resultaat = waarde; binnen elke Verwachtingen blok. Hier kunt u verschillende waarden voor meer dan één aanroep retourneren door alleen maar te gebruiken retourneert (waarde1, waarde2,…, waarden); in plaats van resultaat = waarde;.

Voor oproepen verifiëren voor een mock kun je nieuwe Verificaties gebruiken() {{mock.call (waarde)}} of nieuwe verificaties (mock) {{}} om elke verwachte oproep die eerder is gedefinieerd, te verifiëren.

Voor het verifiëren van argumentenkunt u specifieke waarden doorgeven, of u heeft vooraf gedefinieerde waarden zoals ieder, anyString, anyLong, en nog veel meer van dat soort speciale waarden en opnieuw de mogelijkheid om je matchers te definiëren (dat moeten Hamcrest-matchers zijn).

@Test openbare void assertTwoMethodsHaveBeenCalled () {UserForm userForm = nieuw UserForm (); userForm.username = "foo"; nieuwe verwachtingen () {{loginService.login (userForm); resultaat = waar; loginService.setCurrentUser ("foo"); }}; String login = loginController.login (userForm); Assert.assertEquals ("OK", login); nieuwe FullVerifications (loginService) {}; } @Test openbare void assertOnlyOneMethodHasBeenCalled () {UserForm userForm = nieuw UserForm (); userForm.username = "foo"; nieuwe verwachtingen () {{loginService.login (userForm); resultaat = onwaar; // geen verwachting voor setCurrentUser}}; String login = loginController.login (userForm); Assert.assertEquals ("KO", login); nieuwe FullVerifications (loginService) {}; }

6. Spottende uitzondering Throwing

6.1. Mockito

Uitzonderlijk gooien kan worden bespot met .thenThrow (ExceptionClass.class) na een Mockito.when (mock.method (args)).

@Test openbare ongeldige mockExceptionThrowin () {UserForm userForm = nieuw UserForm (); Mockito.when (loginService.login (userForm)). ThenThrow (IllegalArgumentException.class); String login = loginController.login (userForm); Assert.assertEquals ("ERROR", login); Mockito.verify (loginService) .login (userForm); Mockito.verifyZeroInteractions (loginService); }

6.2. EasyMock

Uitzonderlijk gooien kan worden bespot met .andThrow (nieuwe ExceptionClass ()) na een EasyMock.expect (...) bellen.

@Test openbare ongeldige mockExceptionThrowing () {UserForm userForm = nieuw UserForm (); EasyMock.expect (loginService.login (userForm)). AndThrow (nieuwe IllegalArgumentException ()); EasyMock.replay (loginService); String login = loginController.login (userForm); Assert.assertEquals ("ERROR", login); EasyMock.verify (loginService); }

6.3. JMockit

Het bespotten van exception throwing met JMockito is bijzonder eenvoudig. Retourneer gewoon een uitzondering als het resultaat van een bespotte methodeaanroep in plaats van de "normale" terugkeer.

@Test openbare ongeldige mockExceptionThrowing () {UserForm userForm = nieuw UserForm (); nieuwe verwachtingen () {{loginService.login (userForm); result = nieuwe IllegalArgumentException (); // geen verwachting voor setCurrentUser}}; String login = loginController.login (userForm); Assert.assertEquals ("ERROR", login); nieuwe FullVerifications (loginService) {}; }

7. Bespotten van een object om rond te geven

7.1. Mockito

U kunt ook een mock maken om door te geven als argument voor een methodeaanroep. Met Mockito kunt u dat doen met een oneliner.

@Test openbare leegte mockAnObjectToPassAround () {UserForm userForm = Mockito.when (Mockito.mock (UserForm.class) .getUsername ()) .thenReturn ("foo"). GetMock (); Mockito.when (loginService.login (userForm)). ThenReturn (true); String login = loginController.login (userForm); Assert.assertEquals ("OK", login); Mockito.verify (loginService) .login (userForm); Mockito.verify (loginService) .setCurrentUser ("foo"); }

7.2. EasyMock

Mocks kunnen worden gemaakt in lijn met EasyMock.mock (Class.class). Daarna kunt u gebruiken EasyMock.expect (mock.method ()) om het voor te bereiden voor executie, altijd denkend om te bellen EasyMock.replay (mock) voordat u het gebruikt.

@Test openbare leegte mockAnObjectToPassAround () {UserForm userForm = EasyMock.mock (UserForm.class); EasyMock.expect (userForm.getUsername ()). AndReturn ("foo"); EasyMock.expect (loginService.login (userForm)). AndReturn (true); loginService.setCurrentUser ("foo"); EasyMock.replay (userForm); EasyMock.replay (loginService); String login = loginController.login (userForm); Assert.assertEquals ("OK", login); EasyMock.verify (userForm); EasyMock.verify (loginService); }

7.3. JMockit

Om een ​​object voor slechts één methode te bespotten, kunt u het eenvoudig als parameter doorgeven aan de testmethode. Dan kun je verwachtingen wekken zoals bij elke andere mock.

@Test openbare ongeldige mockAnObjectToPassAround (@Mocked UserForm userForm) {nieuwe verwachtingen () {{userForm.getUsername (); result = "foo"; loginService.login (userForm); resultaat = waar; loginService.setCurrentUser ("foo"); }}; String login = loginController.login (userForm); Assert.assertEquals ("OK", login); nieuwe FullVerifications (loginService) {}; nieuwe FullVerifications (userForm) {}; }

8. Aangepaste argumentafstemming

8.1. Mockito

Soms moet het matchen van argumenten voor bespotte oproepen iets complexer zijn dan alleen een vaste waarde of anyString (). Voor die gevallen heeft Mockito zijn matcherklasse die wordt gebruikt met argThat (ArgumentMatcher).

@Test openbare ongeldige argumentMatching () {UserForm userForm = nieuw UserForm (); userForm.username = "foo"; // standaard matcher Mockito.when (loginService.login (Mockito.any (UserForm.class))). thenReturn (true); String login = loginController.login (userForm); Assert.assertEquals ("OK", login); Mockito.verify (loginService) .login (userForm); // complexe matcher Mockito.verify (loginService) .setCurrentUser (ArgumentMatchers.argThat (nieuwe ArgumentMatcher () {@Override openbare booleaanse overeenkomsten (String-argument) {return argument.startsWith ("foo");}})); }

8.2. EasyMock

Het matchen van aangepaste argumenten is iets gecompliceerder met EasyMock, omdat u een statische methode moet maken waarin u de daadwerkelijke matcher maakt en deze vervolgens rapporteert met EasyMock.reportMatcher (IArgumentMatcher).

Zodra deze methode is gemaakt, gebruikt u deze volgens uw schijnverwachtingen met een aanroep van de methode (zoals te zien in het voorbeeld in de regel).

@Test openbare ongeldige argumentMatching () {UserForm userForm = nieuw UserForm (); userForm.username = "foo"; // standaard matcher EasyMock.expect (loginService.login (EasyMock.isA (UserForm.class))). andReturn (true); // complexe matcher loginService.setCurrentUser (specificArgumentMatching ("foo")); EasyMock.replay (loginService); String login = loginController.login (userForm); Assert.assertEquals ("OK", login); EasyMock.verify (loginService); } private static String specificArgumentMatching (String verwacht) {EasyMock.reportMatcher (nieuwe IArgumentMatcher () {@Override public boolean matches (Object argument) {return argument instanceof String && ((String) argument) .startsWith (verwacht);} @Override public void appendTo (StringBuffer-buffer) {// NOOP}}); null teruggeven; }

8.3. JMockit

Aangepaste argumentafstemming met JMockit wordt gedaan met de special withArgThat (Matcher) methode (die Hamcrest's Matcher voorwerpen).

@Test openbare ongeldige argumentMatching () {UserForm userForm = nieuw UserForm (); userForm.username = "foo"; // standaard matcher nieuwe Expectations () {{loginService.login ((UserForm) any); resultaat = waar; // complexe matcher loginService.setCurrentUser (withArgThat (new BaseMatcher () {@Override public boolean matches (Object item) {return item instanceof String && ((String) item) .startsWith ("foo");} @Override public void descriptionTo (Beschrijving beschrijving) {// NOOP}})); }}; String login = loginController.login (userForm); Assert.assertEquals ("OK", login); nieuwe FullVerifications (loginService) {}; }

9. Gedeeltelijk bespotten

9.1. Mockito

Mockito staat gedeeltelijke bespotting toe (een mock die de echte implementatie gebruikt in plaats van bespotte methodeaanroepen in sommige van zijn methoden) op twee manieren.

U kunt beide gebruiken .thenCallRealMethod () in een normale aanroepdefinitie van een nepmethode, of u kunt een spion in plaats van een mock, in welk geval het standaardgedrag daarvoor zal zijn om de echte implementatie aan te roepen in alle niet-bespotte methoden.

@Test public void PartialMocking () {// gebruik gedeeltelijke mock loginController.loginService = spiedLoginService; UserForm userForm = nieuw UserForm (); userForm.username = "foo"; // laat het inloggen van de service de implementatie gebruiken, dus laten we DAO-aanroep Mockito.when (loginugeot.login (userForm)). thenReturn (1) bespotten; String login = loginController.login (userForm); Assert.assertEquals ("OK", login); // verifieer bespotte oproep Mockito.verify (spiedLoginService) .setCurrentUser ("foo"); }

9.2. EasyMock

Gedeeltelijk bespotten wordt ook iets gecompliceerder met EasyMock, omdat u moet bepalen welke methoden worden bespot bij het maken van de mock-up.

Dit wordt gedaan met EasyMock.partialMockBuilder (Class.class) .addMockedMethod ("methodName"). CreateMock (). Zodra dit is gebeurd, kunt u de mock gebruiken als elke andere niet-gedeeltelijke mock.

@Test openbare ongeldige gedeeltelijkeMocking () {UserForm userForm = nieuw UserForm (); userForm.username = "foo"; // gebruik gedeeltelijke mock LoginService loginServicePartial = EasyMock.partialMockBuilder (LoginService.class) .addMockedMethod ("setCurrentUser"). createMock (); loginServicePartial.setCurrentUser ("foo"); // laat het inloggen van de service de implementatie gebruiken, dus laten we DAO belachelijk maken, EasyMock.expect (loginugeot.login (userForm)). andReturn (1); loginServicePartial.setLoginão (loginugeot); loginController.loginService = loginServicePartial; EasyMock.replay (login Dao); EasyMock.replay (loginServicePartial); String login = loginController.login (userForm); Assert.assertEquals ("OK", login); // verifieer bespotte oproep EasyMock.verify (loginServicePartial); EasyMock.verify (loginão); }

9.3. JMockit

Gedeeltelijk bespotten met JMockit is bijzonder eenvoudig. Elke methodeaanroep waarvoor geen bespot gedrag is gedefinieerd in een Verwachtingen () {{}} toepassingen de "echte" implementatie.

Laten we ons nu eens voorstellen dat we de LoginService klasse om de setCurrentUser () methode tijdens het gebruik van de daadwerkelijke implementatie van de Log in() methode.

Om dit te doen, maken we eerst een instantie van LoginService aan het verwachtingenblok. Vervolgens leggen we alleen een verwachting vast voor de setCurrentUser () methode:

@Test openbare ongeldige gedeeltelijkeMocking () {LoginService gedeeltelijkeLoginService = nieuwe LoginService (); partiëleLoginService.setLoginão (loginugeot); loginController.loginService = gedeeltelijkeLoginService; UserForm userForm = nieuw UserForm (); userForm.username = "foo"; new Expectations (PartialLoginService) {{// laten we DAO-aanroep login Dao.login (userForm) bespotten; resultaat = 1; // geen verwachting voor inlogmethode zodat echte implementatie wordt gebruikt // mock setCurrentUser aanroep partialLoginService.setCurrentUser ("foo"); }}; String login = loginController.login (userForm); Assert.assertEquals ("OK", login); // verifieer bespotte oproep nieuwe Verificaties () {{PartialLoginService.setCurrentUser ("foo"); }}; }

10. Conclusie

In dit bericht hebben we drie Java-mock-bibliotheken vergeleken, elk met zijn sterke punten en nadelen.

  • Ze zijn alle drie eenvoudig geconfigureerd met annotaties om u te helpen spotprenten en het te testen object te definiëren, met hardlopers om nepinjectie zo pijnloos mogelijk te maken.
    • We zouden zeggen dat Mockito hier zou winnen omdat het een speciale annotatie heeft voor gedeeltelijke spot, maar JMockit heeft het niet eens nodig, dus laten we zeggen dat het een gelijkspel is tussen die twee.
  • Ze volgen alle drie min of meer de record-replay-verifieer patroon, maar naar onze mening is JMockit de beste manier om dit te doen, omdat het je dwingt om die in blokken te gebruiken, zodat tests meer gestructureerd worden.
  • Gemak van gebruik is belangrijk, zodat u zo min mogelijk kunt werken om uw tests te definiëren. JMockit zal de gekozen optie zijn vanwege zijn vaste-altijd-dezelfde-structuur.
  • Mockito is min of meer DE meest bekende, zodat de gemeenschap zal groter zijn.
  • Moeten bellen herhaling elke keer dat je een mock wilt gebruiken, is een duidelijk niet gaan, dus we zetten een min voor EasyMock.
  • Consistentie / eenvoud is ook belangrijk voor mij. We hielden van de manier om resultaten van JMockit te retourneren die hetzelfde is voor "normale" resultaten als voor uitzonderingen.

Als dit alles gezegd is, zullen we kiezen JMockit als een soort winnaar, ook al hebben we tot nu toe gebruikt Mockito zoals we zijn gefascineerd door zijn eenvoud en vaste structuur en we zullen het vanaf nu proberen te gebruiken.

De volledige implementatie van deze tutorial is te vinden op het GitHub-project, dus voel je vrij om het te downloaden en ermee te spelen.