JMockit geavanceerd gebruik

1. Inleiding

In dit artikel gaan we verder dan de basisprincipes van JMockit en gaan we kijken naar enkele geavanceerde scenario's, zoals:

  • Vervalsing (of de MockUp API)
  • De Ontkapseling hulpprogramma klasse
  • Hoe u meer dan één interface kunt bespotten met slechts één mock
  • Hoe u verwachtingen en verificaties kunt hergebruiken

Als je de basis van JMockit wilt ontdekken, bekijk dan andere artikelen uit deze serie. Onderaan de pagina vindt u relevante links.

2. Maven Afhankelijkheid

Eerst moeten we de jmockit-afhankelijkheid aan ons project toevoegen:

 org.jmockit jmockit 1.41 

Vervolgens gaan we verder met de voorbeelden.

3. Private methoden / innerlijke klassen bespotten

Het bespotten en testen van privémethoden of innerlijke klassen wordt vaak niet als een goede praktijk beschouwd.

De redenering erachter is dat als ze privé zijn, ze niet direct moeten worden getest, omdat ze het diepste lef van de klas zijn, maar soms moet het nog steeds worden gedaan, vooral als het gaat om legacy-code.

Met JMockit heb je twee opties om deze af te handelen:

  • De MockUp API om de echte implementatie te wijzigen (voor het tweede geval)
  • De Ontkapseling utility class, om elke methode rechtstreeks aan te roepen (voor het eerste geval)

Alle volgende voorbeelden worden gedaan voor de volgende klasse en we nemen aan dat deze worden uitgevoerd op een testklasse met dezelfde configuratie als de eerste (om herhaling van code te voorkomen):

openbare klasse AdvancedCollaborator {int i; privé int privateField = 5; // standaard constructor weggelaten public AdvancedCollaborator (String string) gooit uitzondering {i = string.length (); } public String methodThatCallsPrivateMethod (int i) {return privateMethod () + i; } openbare int methodThatReturnsThePrivateField () {return privateField; } private String privateMethod () {return "default:"; } class InnerAdvancedCollaborator {...}}

3.1. Faken met MockUp

De Mockup-API van JMockit biedt ondersteuning voor het maken van nep-implementaties of mock-ups. Typisch een mock-up richt zich op een paar methoden en / of constructors in de klasse die moeten worden vervalst, terwijl de meeste andere methoden en constructors ongewijzigd blijven. Dit zorgt voor een volledig herschrijven van een klasse, zodat elke methode of constructor (met elke toegangsmodificator) kan worden getarget.

Laten we eens kijken hoe we het opnieuw kunnen definiëren privateMethod () met behulp van de API van de Mockup:

@RunWith (JMockit.class) openbare klasse AdvancedCollaboratorTest {@Tested privé AdvancedCollaborator-mock; @Test openbare ongeldige testToMockUpPrivateMethod () {nieuwe MockUp () {@Mock private String privateMethod () {retourneer "bespot:"; }}; String res = mock.methodThatCallsPrivateMethod (1); assertEquals ("bespot: 1", res); }}

In dit voorbeeld definiëren we een nieuw MockUp voor de AdvancedCollaborator klasse met behulp van de @Bespotten annotatie op een methode met bijpassende handtekening. Hierna worden aanroepen naar die methode gedelegeerd naar onze bespotte.

We kunnen dit ook gebruiken om mock-up de constructor van een klasse die specifieke argumenten of configuratie nodig heeft om tests te vereenvoudigen:

@Test openbare ongeldige testToMockUpDifficultConstructor () gooit uitzondering {nieuwe MockUp () {@Mock openbare ongeldige $ init (aanroepaanroep, tekenreeks) {((AdvancedCollaborator) aanroep.getInvokedInstance ()). I = 1; }}; AdvancedCollaborator coll = nieuwe AdvancedCollaborator (null); assertEquals (1, coll. i); }

In dit voorbeeld kunnen we zien dat je voor het bespotten van constructeurs het $ init methode. U kunt een extra type argument doorgeven Aanroeping, waarmee u toegang krijgt tot informatie over de aanroep van de bespotte methode, inclusief de instantie waarnaar de aanroep wordt uitgevoerd.

3.2. De ... gebruiken Ontkapseling Klasse

JMockit bevat een testhulpprogramma-klasse: het Ontkapseling. Zoals de naam al aangeeft, wordt het gebruikt om een ​​staat van een object te de-inkapselen, en door het te gebruiken, kunt u het testen vereenvoudigen door velden en methoden te openen die anders niet toegankelijk waren.

U kunt een methode aanroepen:

@Test openbare ongeldige testToCallPrivateMethodsDirectly () {Objectwaarde = Deencapsulation.invoke (mock, "privateMethod"); assertEquals ("default:", waarde); }

U kunt ook velden instellen:

@Test openbare ongeldige testToSetPrivateFieldDirectly () {Deencapsulation.setField (mock, "privateField", 10); assertEquals (10, mock.methodThatReturnsThePrivateField ()); }

En ontvang velden:

@Test openbare ongeldige testToGetPrivateFieldDirectly () {int waarde = Deencapsulation.getField (mock, "privateField"); assertEquals (5, waarde); }

En maak nieuwe instanties van klassen:

@Test openbare ongeldige testToCreateNewInstanceDirectly () {AdvancedCollaborator coll = Deencapsulation .newInstance (AdvancedCollaborator.class, "foo"); assertEquals (3, coll. i); }

Zelfs nieuwe voorbeelden van innerlijke klassen:

@Test openbare ongeldige testToCreateNewInnerClassInstanceDirectly () {InnerCollaborator inner = Deencapsulation .newInnerInstance (InnerCollaborator.class, mock); assertNotNull (innerlijke); }

Zoals u kunt zien, is de Ontkapseling klasse is uitermate handig bij het testen van luchtdichte klassen. Een voorbeeld zou kunnen zijn om afhankelijkheden in te stellen van een klasse die @Autowired annotaties op privévelden en heeft er geen setters voor, of om innerlijke klassen te testen zonder afhankelijk te zijn van de openbare interface van de containerklasse.

4. Meerdere interfaces bespotten in éénzelfde mock-up

Laten we aannemen dat u een klasse wilt testen - nog niet geïmplementeerd - maar u weet zeker dat deze verschillende interfaces zal implementeren.

Normaal gesproken zou je de genoemde klasse niet kunnen testen voordat je deze implementeert, maar met JMockit heb je de mogelijkheid om vooraf tests voor te bereiden door meer dan één interface te bespotten met één nepobject.

Dit kan worden bereikt door generieke termen te gebruiken en een type te definiëren dat meerdere interfaces uitbreidt. Dit generieke type kan worden gedefinieerd voor een hele testklasse of voor slechts één testmethode.

We gaan bijvoorbeeld een mock-up maken voor interfaces Lijst en Vergelijkbaar twee manieren:

@RunWith (JMockit.class) openbare klasse AdvancedCollaboratorTest> {@Mocked privé MultiMock multiMock; @Test openbare ongeldige testOnClass () {nieuwe verwachtingen () {{multiMock.get (5); result = "foo"; multiMock.compareTo ((List) any); resultaat = 0; }}; assertEquals ("foo", multiMock.get (5)); assertEquals (0, multiMock.compareTo (new ArrayList ())); } @Test openbaar > ongeldig testOnMethod (@Mocked M mock) {new Expectations () {{mock.get (5); result = "foo"; mock.compareTo ((List) any); resultaat = 0; }}; assertEquals ("foo", mock.get (5)); assertEquals (0, mock.compareTo (new ArrayList ())); }}

Zoals je in regel 2 kunt zien, kunnen we een nieuw testtype voor de hele test definiëren door generieke termen voor de klassenaam te gebruiken. Op die manier MultiMock zal beschikbaar zijn als een type en je kunt er nep voor maken met behulp van een van de annotaties van JMockit.

In de regels van 7 tot 18 zien we een voorbeeld met een mock van een multi-klasse die voor de hele testklasse is gedefinieerd.

Als je de multi-interface mock voor slechts één test nodig hebt, kun je dit bereiken door het generieke type op de methodehandtekening te definiëren en een nieuwe mock van die nieuwe generiek door te geven als het argument van de testmethode. In de regels 20 tot 32 zien we een voorbeeld van hetzelfde geteste gedrag als in de vorige test.

5. Hergebruik van verwachtingen en verificaties

Bij het testen van klassen kunt u uiteindelijk gevallen tegenkomen waarin u hetzelfde herhaalt Verwachtingen en / of Verificaties opnieuw en opnieuw. Om dat te vergemakkelijken, kunt u beide gemakkelijk hergebruiken.

We gaan het uitleggen aan de hand van een voorbeeld (we gebruiken de klassen Model, medewerker, en Uitvoerder uit ons JMockit 101-artikel):

@RunWith (JMockit.class) openbare klasse ReusingTest {@Injectable private Collaborator-medewerker; @Mocked privémodelmodel; @ Geteste privé performer; @Before public void setup () {new Expectations () {{model.getInfo (); result = "foo"; minTimes = 0; collaborator.collaborate ("foo"); resultaat = waar; minTimes = 0; }}; } @Test openbare ongeldige testWithSetup () {performer.perform (model); verifiërenTrueCalls (1); } beschermde ongeldige verificatieTrueCalls (int oproepen) {nieuwe Verificaties () {{collaborator.receive (true); times = oproepen; }}; } laatste klasse TrueCallsVerification breidt Verificaties uit {openbare TrueCallsVerification (int aanroepen) {collaborator.receive (true); times = oproepen; }} @Test openbare ongeldige testWithFinalClass () {performer.perform (model); nieuwe TrueCallsVerification (1); }}

In dit voorbeeld kun je in regels van 15 tot 18 zien dat we voor elke test een verwachting voorbereiden, zodat model.getInfo () keert altijd terug "Foo" en voor collaborator. samenwerken() altijd verwachten "Foo" als argument en terug waar. We zetten de minTimes = 0 verklaring, zodat er geen mislukkingen verschijnen wanneer ze niet daadwerkelijk in tests worden gebruikt.

We hebben ook een methode gemaakt verifiërenTrueCalls (int) om verificaties naar het collaborator.receive (boolean) methode als het doorgegeven argument is waar.

Ten slotte kunt u ook nieuwe soorten specifieke verwachtingen en verificaties creëren die slechts een van de Verwachtingen of Verificaties klassen. Vervolgens definieer je een constructor als je het gedrag moet configureren en een nieuw exemplaar van dat type in een test moet maken, zoals we doen in de regels van 33 tot 43.

6. Conclusie

Met deze aflevering van de JMockit-serie hebben we verschillende geavanceerde onderwerpen aangeroerd die je zeker zullen helpen bij het bespotten en testen van elke dag.

Mogelijk doen we meer artikelen over JMockit, dus blijf op de hoogte voor nog meer informatie.

En, zoals altijd, is de volledige implementatie van deze tutorial te vinden op GitHub.

6.1. Artikelen in de serie

Alle artikelen uit de serie:

  • JMockit 101
  • Een gids voor JMockit-verwachtingen
  • JMockit geavanceerd gebruik