Inleiding tot EasyMock

1. Inleiding

In het verleden hebben we uitgebreid gesproken over JMockit en Mockito.

In deze tutorial geven we een inleiding tot een andere mocking-tool - EasyMock.

2. Maven afhankelijkheden

Voordat we erin duiken, voegen we de volgende afhankelijkheid toe aan onze pom.xml:

 org.easymock easymock 3.5.1 test 

De laatste versie is hier altijd te vinden.

3. Kernconcepten

Bij het genereren van een mock, we kunnen het doelobject simuleren, zijn gedrag specificeren en tenslotte verifiëren of het wordt gebruikt zoals verwacht.

Werken met de spotjes van EasyMock omvat vier stappen:

  1. het creëren van een mock van de doelklasse
  2. het vastleggen van het verwachte gedrag, inclusief de actie, het resultaat, uitzonderingen, enz.
  3. bespottingen gebruiken in tests
  4. controleren of het zich gedraagt ​​zoals verwacht

Nadat onze opname is voltooid, schakelen we deze over naar de "replay" -modus, zodat de mock zich gedraagt ​​zoals opgenomen wanneer we samenwerken met een object dat deze zal gebruiken.

Uiteindelijk controleren we of alles naar verwachting verloopt.

De vier hierboven genoemde stappen hebben betrekking op methoden in org.easymock.EasyMock:

  1. bespotten(…): genereert een mock van de doelklasse, of het nu een concrete klasse of een interface is. Eenmaal gemaakt, bevindt een mock-up zich in de "opname" -modus, wat betekent dat EasyMock elke actie van het mock-object zal opnemen en deze opnieuw zal afspelen in de "replay" -modus
  2. verwachten(…): met deze methode kunnen we verwachtingen scheppen, inclusief oproepen, resultaten en uitzonderingen, voor bijbehorende opnameacties
  3. herhaling(…): schakelt een gegeven mock-up over naar de modus voor opnieuw afspelen. Vervolgens zal elke actie die eerder opgenomen methodeaanroepen triggert 'opgenomen resultaten' opnieuw afspelen
  4. verifiëren(…): controleert of aan alle verwachtingen is voldaan en dat er geen onverwachte oproep is uitgevoerd tijdens een mock-up

In de volgende sectie laten we zien hoe deze stappen in actie werken aan de hand van praktijkvoorbeelden.

4. Een praktisch voorbeeld van bespotten

Laten we, voordat we verder gaan, eens kijken naar de voorbeeldcontext: stel dat we een lezer van de Baeldung-blog hebben, die graag door artikelen op de website bladert, en dan probeert hij / zij artikelen te schrijven.

Laten we beginnen met het maken van het volgende model:

openbare klasse BaeldungReader {privé ArticleReader articleReader; privé IArticleWriter articleWriter; // openbare constructeurs BaeldungArticle readNext () {return articleReader.next (); } openbare lijst readTopic (String topic) {return articleReader.ofTopic (topic); } public String write (String titel, String inhoud) {return articleWriter.write (titel, inhoud); }}

In dit model hebben we twee particuliere leden: de artikelReader(een concrete klasse) en de artikelWriter (een interface).

Vervolgens zullen we ze bespotten om te verifiëren BaeldungReader'S gedrag.

5. Bespotten met Java-code

Laten we beginnen met het bespotten van een Artikellezer.

5.1. Typisch spottend

We verwachten de artikelReader.next () methode die moet worden aangeroepen wanneer een lezer een artikel overslaat:

@Test openbare leegte whenReadNext_thenNextArticleRead () {ArticleReader mockArticleReader = mock (ArticleReader.class); BaeldungReader baeldungReader = nieuwe BaeldungReader (mockArticleReader); verwachten (mockArticleReader.next ()). andReturn (null); replay (mockArticleReader); baeldungReader.readNext (); verifiëren (mockArticleReader); }

In de voorbeeldcode hierboven houden we ons strikt aan de 4-stappenprocedure en bespotten we de Artikellezer klasse.

Hoewel het ons echt niet kan schelen wat mockArticleReader.next () retourneert, moeten we nog steeds een retourwaarde opgeven voor mockArticleReader.next () door het gebruiken van verwachten (…) .enReturn (…).

Met verwachten(…)Verwacht EasyMock dat de methode een waarde retourneert of een Uitzondering.

Als we gewoon doen:

mockArticleReader.next (); replay (mockArticleReader);

EasyMock zal hierover klagen, omdat het een oproep vereist verwachten (...) .enReturn (...) als de methode iets teruggeeft.

Als het een leegte methode, kunnen we verwachten zijn actie met behulp van verwachtenLastCall () soortgelijk:

mockArticleReader.someVoidMethod (); verwachtenLastCall (); replay (mockArticleReader);

5.2. Volgorde opnieuw afspelen

Als we acties nodig hebben die in een specifieke volgorde moeten worden afgespeeld, kunnen we strenger zijn:

@Test openbare leegte whenReadNextAndSkimTopics_thenAllAllowed () {ArticleReader mockArticleReader = strictMock (ArticleReader.class); BaeldungReade baeldungReader = nieuwe BaeldungReader (mockArticleReader); verwachten (mockArticleReader.next ()). andReturn (null); verwachten (mockArticleReader.ofTopic ("easymock")). andReturn (null); replay (mockArticleReader); baeldungReader.readNext (); baeldungReader.readTopic ("easymock"); verifiëren (mockArticleReader); }

In dit fragment hebben we gebruik strictMock (...) om de volgorde van methodeaanroepen te controleren. Voor spot gemaakt door bespotten(…) en strictMock (...), zou elke onverwachte methode-aanroep een AssertionFout.

Om elke methodeaanroep voor de mock toe te staan, kunnen we gebruiken niceMock (...):

@Test openbare leegte whenReadNextAndOthers_thenAllowed () {ArticleReader mockArticleReader = niceMock (ArticleReader.class); BaeldungReade baeldungReader = nieuwe BaeldungReader (mockArticleReader); verwachten (mockArticleReader.next ()). andReturn (null); replay (mockArticleReader); baeldungReader.readNext (); baeldungReader.readTopic ("easymock"); verifiëren (mockArticleReader); }

Hier hadden we de baeldungReader.readTopic (…) gebeld worden, maar EasyMock zal niet klagen. Met niceMock (...), EasyMock maakt het nu alleen uit of het doelobject de verwachte actie heeft uitgevoerd of niet.

5.3. Spottend Uitzondering Gooit

Laten we nu doorgaan met het bespotten van de interface IArticleWriter, en hoe om te gaan met verwachte Werpspullen:

@Test openbare leegte whenWriteMaliciousContent_thenArgumentIllegal () {// bespotting en initialisatie verwachten (mockArticleWriter .write ("easymock", "")) .andThrow (nieuwe IllegalArgumentException ()); replay (mockArticleWriter); // schrijf kwaadaardige inhoud en leg de uitzondering vast zoals verwacht Uitzondering verifiëren (mockArticleWriter); assertEquals (IllegalArgumentException.class, verwachtException.getClass ()); }

In het bovenstaande fragment verwachten we de artikelWriter is solide genoeg om XSS-aanvallen (Cross-site Scripting) te detecteren.

Dus wanneer de lezer kwaadaardige code in de artikelinhoud probeert te injecteren, moet de schrijver een IllegalArgumentException. We hebben dit verwachte gedrag vastgelegd met verwachten (...) .enGooi (...).

6. Bespotten met annotatie

EasyMock ondersteunt ook het injecteren van spot met annotaties. Om ze te gebruiken, moeten we onze unit-tests uitvoeren met EasyMockRunner zodat het verwerkt @Bespotten en @Proef persoon annotaties.

Laten we eerdere fragmenten herschrijven:

@RunWith (EasyMockRunner.class) openbare klasse BaeldungReaderAnnotatedTest {@Mock ArticleReader mockArticleReader; @TestSubject BaeldungReader baeldungReader = nieuwe BaeldungReader (); @Test openbare leegte whenReadNext_thenNextArticleRead () {verwacht (mockArticleReader.next ()). AndReturn (null); replay (mockArticleReader); baeldungReader.readNext (); verifiëren (mockArticleReader); }}

Gelijk aan bespotten(…), wordt een mock-up geïnjecteerd in velden die zijn geannoteerd met @Bespotten. En deze bespotten zullen worden geïnjecteerd in velden van de klasse die is geannoteerd met @Proef persoon.

In het bovenstaande fragment hebben we het artikelReader veld in baeldungReader. Bij het bellen baeldungReader.readNext (), kunnen we dat impliciet genoemd mockArticleReader.

Dat was omdat mockArticleReader werd geïnjecteerd de articleReader veld.

Merk op dat als we een andere testrunner willen gebruiken in plaats van EasyMockRunner, kunnen we de JUnit-testregel gebruiken EasyMockRule:

openbare klasse BaeldungReaderAnnotatedWithRuleTest {@Rule openbaar EasyMockRule mockRule = nieuwe EasyMockRule (dit); // ... @Test openbare leegte whenReadNext_thenNextArticleRead () {verwacht (mockArticleReader.next ()). AndReturn (null); replay (mockArticleReader); baeldungReader.readNext (); verifiëren (mockArticleReader); }}

7. Bespotten met EasyMockSupport

Soms moeten we meerdere mock-ups in een enkele test introduceren, en we moeten dit handmatig herhalen:

opnieuw afspelen (A); opnieuw afspelen (B); opnieuw afspelen (C); // ... verifieer (A); verifieer (B); verifieer (C);

Dit is lelijk en we hebben een elegante oplossing nodig.

Gelukkig hebben we een les EasyMockSupport in EasyMock om hiermee om te gaan. Het helpt spotjes bij te houden, zodat we ze opnieuw kunnen afspelen en verifiëren in een batch als deze:

// ... openbare klasse BaeldungReaderMockSupportTest breidt EasyMockSupport uit {// ... @Test openbare leegte whenReadAndWriteSequencially_thenWorks () {verwacht (mockArticleReader.next ()). andReturn (null) .times (2) .andThrow (nieuwe NoSuchElementException ()) ; verwachten (mockArticleWriter.write ("title", "content")) .andReturn ("BAEL-201801"); speel alles opnieuw af(); // voer achtereenvolgens lees- en schrijfbewerkingen uit verifyAll (); assertEquals (NoSuchElementException.class, verwachteException.getClass ()); assertEquals ("BAEL-201801", articleId); }}

Hier bespotten we beide artikelReader en artikelWriter. Bij het instellen van deze bespottingen in de "replay" -modus, hebben we een statische methode gebruikt speel alles opnieuw af() geleverd door EasyMockSupport, en gebruikt verifiërenAll () om hun gedrag in batch te verifiëren.

We hebben ook geïntroduceerd keer(…) methode in de verwachten fase. Het helpt specificeren hoe vaak we verwachten dat de methode wordt aangeroepen, zodat we kunnen voorkomen dat er dubbele code wordt ingevoerd.

We kunnen ook gebruik maken van EasyMockSupport via delegatie:

EasyMockSupport easyMockSupport = nieuwe EasyMockSupport (); @Test openbare leegte whenReadAndWriteSequencially_thenWorks () {ArticleReader mockArticleReader = easyMockSupport .createMock (ArticleReader.class); IArticleWriter mockArticleWriter = easyMockSupport .createMock (IArticleWriter.class); BaeldungReader baeldungReader = nieuwe BaeldungReader (mockArticleReader, mockArticleWriter); verwachten (mockArticleReader.next ()). andReturn (null); verwachten (mockArticleWriter.write ("title", "content")) .andReturn (""); easyMockSupport.replayAll (); baeldungReader.readNext (); baeldungReader.write ("titel", "inhoud"); easyMockSupport.verifyAll (); }

Eerder gebruikten we statische methoden of annotaties om schijnvertoningen te maken en te beheren. Onder de motorkap worden deze statische en geannoteerde spotten gecontroleerd door een global EasyMockSupport voorbeeld.

Hier hebben we het expliciet geïnstantieerd en nemen we al deze bespotten onder onze eigen controle, door middel van delegatie. Dit kan verwarring helpen voorkomen als er naamconflicten zijn in onze testcode met EasyMock of als er soortgelijke gevallen zijn.

8. Conclusie

In dit artikel hebben we kort het basisgebruik van EasyMock geïntroduceerd, over het genereren van nepobjecten, het opnemen en opnieuw afspelen van hun gedrag, en controleren of ze zich correct gedroegen.

Als je misschien geïnteresseerd bent, bekijk dan dit artikel voor een vergelijking van EasyMock, Mocket en JMockit.

Zoals altijd is de volledige implementatie te vinden op Github.