JMockit 101

1. Einleitung

Mit diesem Artikel starten wir eine neue Serie, die sich um das Spott-Toolkit JMockit dreht.

In diesem ersten Teil werden wir darüber sprechen, was JMockit ist, welche Eigenschaften es hat und wie Mocks damit erstellt und verwendet werden.

Spätere Artikel werden sich auf seine Fähigkeiten konzentrieren und diese vertiefen.

2. JMockit

2.1. Einführung

Lassen Sie uns zunächst darüber sprechen, was JMockit ist: ein Java-Framework zum Verspotten von Objekten in Tests (Sie können es sowohl für JUnit- als auch für TestNG-Objekte verwenden).

Es verwendet die Instrumentierungs-APIs von Java, um den Bytecode der Klassen zur Laufzeit zu ändern und ihr Verhalten dynamisch zu ändern. Einige seiner Stärken sind seine Ausdruckskraft und seine sofort einsatzbereite Fähigkeit, statische und private Methoden zu verspotten.

Vielleicht bist du neu bei JMockit, aber es liegt definitiv nicht daran, dass es neu ist. Die Entwicklung von JMockit begann im Juni 2006 und die ersten stabilen Veröffentlichungstermine waren Dezember 2012, es gibt sie also schon seit einiger Zeit (die aktuelle Version ist zum Zeitpunkt des Schreibens des Artikels 1.24).

2.2. Maven-Abhängigkeit

Zuerst müssen wir die jmockit-Abhängigkeit zu unserem Projekt hinzufügen:

 org.jmockit jmockit 1.41 

2.3. Die Expressibilität von JMockit

Wie bereits erwähnt, ist eine der Stärken von JMockit seine Ausdruckskraft. Um Mocks zu erstellen und ihr Verhalten zu definieren, müssen Sie sie nur direkt definieren, anstatt Methoden über die Mocking-API aufzurufen.

Dies bedeutet, dass Sie Dinge wie:

API.expect(mockInstance.method()).andThenReturn(value).times(2);

Erwarten Sie stattdessen Dinge wie:

new Expectation() { mockInstance.method(); result = value; times = 2; }

Es mag scheinen, dass es mehr Code ist, aber Sie könnten einfach alle drei Zeilen nur in eine setzen. Der wirklich wichtige Teil ist, dass Sie nicht mit einem großen „Zug“ verketteter Methodenaufrufe enden. Stattdessen erhalten Sie eine Definition, wie sich der Mock beim Aufruf verhalten soll.

Wenn Sie berücksichtigen, dass Sie im Ergebnis = Wert- Teil alles zurückgeben können (feste Werte, dynamisch generierte Werte, Ausnahmen usw.), wird die Ausdruckskraft von JMockit noch deutlicher.

2.4. Das Record-Replay-Verify-Modell

Tests mit JMockit sind in drei differenzierte Phasen unterteilt: Aufzeichnen, Wiedergeben und Überprüfen.

  1. In der Aufzeichnungsphase , während der Testvorbereitung und vor dem Aufruf der Methoden, die ausgeführt werden sollen, definieren wir das erwartete Verhalten für alle Tests, die in der nächsten Phase verwendet werden sollen.
  2. Die Wiedergabephase ist die Phase, in der der zu testende Code ausgeführt wird. Die Aufrufe von verspotteten Methoden / Konstruktoren, die zuvor in der vorherigen Stufe aufgezeichnet wurden, werden nun wiedergegeben.
  3. Schließlich werden wir in der Überprüfungsphase behaupten, dass das Ergebnis des Tests das erwartete war (und dass sich Mocks gemäß den Definitionen in der Aufzeichnungsphase verhalten haben und verwendet wurden).

Mit einem Codebeispiel würde ein Drahtmodell für einen Test ungefähr so ​​aussehen:

@Test public void testWireframe() { // preparation code not specific to JMockit, if any new Expectations() {{ // define expected behaviour for mocks }}; // execute code-under-test new Verifications() {{ // verify mocks }}; // assertions }

3. Mocks erstellen

3.1. Anmerkungen von JMockit

Wenn Sie JMockit verwenden, können Sie Mocks am einfachsten mithilfe von Anmerkungen verwenden. Es gibt drei zum Erstellen von Mocks ( @Mocked , @Injectable und @Capturing ) und eine zum Angeben der zu testenden Klasse ( @Tested ).

Wenn Sie die Annotation @Mocked für ein Feld verwenden, werden verspottete Instanzen jedes neuen Objekts dieser bestimmten Klasse erstellt.

Andererseits wird mit der Annotation @Injectable nur eine verspottete Instanz erstellt.

Die letzte Annotation, @Capturing , verhält sich wie @Mocked, erweitert jedoch ihre Reichweite auf jede Unterklasse, die den Typ des annotierten Felds erweitert oder implementiert.

3.2. Argumente an Tests übergeben

Bei Verwendung von JMockit ist es möglich, Mocks als Testparameter zu übergeben. Dies ist sehr nützlich, um ein Modell nur für diesen einen Test zu erstellen, z. B. für ein komplexes Modellobjekt, das beispielsweise nur für einen Test ein bestimmtes Verhalten benötigt. Es wäre ungefähr so:

@RunWith(JMockit.class) public class TestPassingArguments { @Injectable private Foo mockForEveryTest; @Tested private Bar bar; @Test public void testExample(@Mocked Xyz mockForJustThisTest) { new Expectations() {{ mockForEveryTest.someMethod("foo"); mockForJustThisTest.someOtherMethod(); }}; bar.codeUnderTest(); } }

Diese Art, ein Modell zu erstellen, indem es als Parameter übergeben wird, anstatt eine API-Methode aufrufen zu müssen, zeigt uns erneut die Ausdruckbarkeit, von der wir von Anfang an sprechen.

3.3. Vollständiges Beispiel

Um diesen Artikel zu beenden, fügen wir ein vollständiges Beispiel eines Tests mit JMockit hinzu.

In diesem Beispiel testen wir eine Performer- Klasse, die in ihrer perform () -Methode Collaborator verwendet. Diese perform () -Methode empfängt ein Model- Objekt als Parameter, von dem sie getInfo () verwendet , das einen String zurückgibt. Dieser String wird von Collaborator an die collaborator () -Methode übergeben , die für diesen bestimmten Test true zurückgibt Dieser Wert wird von Collaborator an die Methode receive () übergeben .

Die getesteten Klassen sehen also folgendermaßen aus:

public class Model { public String getInfo(){ return "info"; } } public class Collaborator { public boolean collaborate(String string){ return false; } public void receive(boolean bool){ // NOOP } } public class Performer { private Collaborator collaborator; public void perform(Model model) { boolean value = collaborator.collaborate(model.getInfo()); collaborator.receive(value); } }

Und der Code des Tests lautet am Ende wie folgt:

@RunWith(JMockit.class) public class PerformerTest { @Injectable private Collaborator collaborator; @Tested private Performer performer; @Test public void testThePerformMethod(@Mocked Model model) { new Expectations() {{ model.getInfo();result = "bar"; collaborator.collaborate("bar"); result = true; }}; performer.perform(model); new Verifications() {{ collaborator.receive(true); }}; } }

4. Fazit

Damit schließen wir unser praktisches Intro zu JMockit ab. Wenn Sie mehr über JMockit erfahren möchten, bleiben Sie auf dem Laufenden für zukünftige Artikel.

Die vollständige Implementierung dieses Tutorials finden Sie im GitHub-Projekt.

4.1. Artikel in der Reihe

Alle Artikel der Reihe:

  • JMockit 101
  • Ein Leitfaden für JMockit - Erwartungen
  • Erweiterte JMockit-Verwendung