Einführung in EasyMock

Einführung in EasyMock

1. Einführung

In der Vergangenheit haben wir ausführlich überJMockit undMockito gesprochen.

In diesem Tutorial geben wir eine Einführung in ein anderes Verspottungswerkzeug -EasyMock.

2. Maven-Abhängigkeiten

Bevor wir eintauchen, fügen wir unserenpom.xml die folgende Abhängigkeit hinzu:


    org.easymock
    easymock
    3.5.1
    test

Die neueste Version ist immerhere zu finden.

3. Kernkonzepte

Beim Generieren eines Mocks werdenwe can simulate the target object, specify its behavior, and finally verify whether it’s used as expected.

Das Arbeiten mit EasyMock-Mocks umfasst vier Schritte:

  1. ein Mock der Zielklasse erstellen

  2. Aufzeichnen des erwarteten Verhaltens, einschließlich der Aktion, des Ergebnisses, der Ausnahmen usw.

  3. mit Mocks in Tests

  4. Überprüfen, ob es sich wie erwartet verhält

Nachdem die Aufnahme beendet ist, schalten wir sie in den Wiedergabemodus, sodass sich der Mock so verhält, wie er aufgezeichnet wurde, wenn Sie mit einem Objekt zusammenarbeiten, das ihn verwenden wird.

Schließlich überprüfen wir, ob alles wie erwartet verläuft.

Die vier oben genannten Schritte beziehen sich auf Methoden inorg.easymock.EasyMock:

  1. mock(…): Erzeugt ein Modell der Zielklasse, sei es eine konkrete Klasse oder eine Schnittstelle. Einmal erstellt, befindet sich ein Mock im Aufnahmemodus, was bedeutet, dass EasyMock alle Aktionen, die das Mock-Objekt ausführt, aufzeichnet und im Wiedergabemodus wiedergibt

  2. expect(…): Mit dieser Methode können wir Erwartungen, einschließlich Aufrufe, Ergebnisse und Ausnahmen, für zugehörige Aufzeichnungsaktionen festlegen

  3. replay(…): Schaltet einen bestimmten Mock in den Wiedergabemodus. Jede Aktion, die zuvor aufgezeichnete Methodenaufrufe auslöst, gibt die aufgezeichneten Ergebnisse wieder.

  4. verify(…): Überprüft, ob alle Erwartungen erfüllt wurden und ob kein unerwarteter Aufruf für einen Schein ausgeführt wurde **

Im nächsten Abschnitt zeigen wir anhand von Beispielen aus der Praxis, wie diese Schritte in Aktion funktionieren.

4. Ein praktisches Beispiel für Spott

Bevor wir fortfahren, werfen wir einen Blick auf den Beispielkontext: Angenommen, wir haben einen Leser des Beispielblogs, der gerne Artikel auf der Website durchsucht, und dann versucht er / sie, Artikel zu schreiben.

Beginnen wir mit der Erstellung des folgenden Modells:

public class exampleReader {

    private ArticleReader articleReader;
    private IArticleWriter articleWriter;

    // constructors

    public exampleArticle readNext(){
        return articleReader.next();
    }

    public List readTopic(String topic){
        return articleReader.ofTopic(topic);
    }

    public String write(String title, String content){
        return articleWriter.write(title, content);
    }
}

In diesem Modell haben wir zwei private Mitglieder:articleReader (eine konkrete Klasse) undarticleWriter (eine Schnittstelle).

Als nächstes werden wir sie verspotten, um das Verhalten vonexampleReaderzu überprüfen.

5. Mock mit Java Code

Beginnen wir mit dem Verspotten vonArticleReader.

5.1. Typisches Verspotten

Wir erwarten, dass die MethodearticleReader.next()aufgerufen wird, wenn ein Leser einen Artikel überspringt:

@Test
public void whenReadNext_thenNextArticleRead(){
    ArticleReader mockArticleReader = mock(ArticleReader.class);
    exampleReader exampleReader
      = new exampleReader(mockArticleReader);

    expect(mockArticleReader.next()).andReturn(null);
    replay(mockArticleReader);

    exampleReader.readNext();

    verify(mockArticleReader);
}

Im obigen Beispielcode halten wir uns strikt an die 4-Stufen-Prozedur und verspotten dieArticleReader-Klasse.

Although we really don’t care what mockArticleReader.next() returns, we still need to specify a return value for mockArticleReader.next() unter Verwendung vonexpect(…).andReturn(…).

Mitexpect(…) erwartet EasyMock, dass die Methode einen Wert zurückgibt oderException. auslöst

Wenn wir einfach machen:

mockArticleReader.next();
replay(mockArticleReader);

EasyMock wird sich darüber beschweren, da es einen Aufruf vonexpect(…).andReturn(…) erfordert, wenn die Methode etwas zurückgibt.

Wenn es sich um einevoid-Methode handelt, können wir die Aktion vonexpectmitexpectLastCall()wie folgt ausführen:

mockArticleReader.someVoidMethod();
expectLastCall();
replay(mockArticleReader);

5.2. Wiederholungsreihenfolge

Wenn Aktionen in einer bestimmten Reihenfolge wiedergegeben werden müssen, können wir strenger vorgehen:

@Test
public void whenReadNextAndSkimTopics_thenAllAllowed(){
    ArticleReader mockArticleReader
      = strictMock(ArticleReader.class);
    exampleReade exampleReader
      = new exampleReader(mockArticleReader);

    expect(mockArticleReader.next()).andReturn(null);
    expect(mockArticleReader.ofTopic("easymock")).andReturn(null);
    replay(mockArticleReader);

    exampleReader.readNext();
    exampleReader.readTopic("easymock");

    verify(mockArticleReader);
}

In diesem Snippet haben wiruse strictMock(…) to check the order of method calls. Bei Mocks, die vonmock(…) undstrictMock(…) erstellt wurden, würden unerwartete MethodenaufrufeAssertionError verursachen.

Um einen Methodenaufruf für den Mock zuzulassen, können wirniceMock(…) verwenden:

@Test
public void whenReadNextAndOthers_thenAllowed(){
    ArticleReader mockArticleReader = niceMock(ArticleReader.class);
    exampleReade exampleReader = new exampleReader(mockArticleReader);

    expect(mockArticleReader.next()).andReturn(null);
    replay(mockArticleReader);

    exampleReader.readNext();
    exampleReader.readTopic("easymock");

    verify(mockArticleReader);
}

Hier haben wir nicht erwartet, dassexampleReader.readTopic(…)aufgerufen werden, aber EasyMock wird sich nicht beschweren. MitniceMock(…), kümmert sich EasyMock jetzt nur noch darum, ob das Zielobjekt die erwartete Aktion ausgeführt hat oder nicht.

5.3. Exception Würfe verspotten

Lassen Sie uns nun mit dem Verspotten der SchnittstelleIArticleWriter und dem Umgang mit erwartetenThrowables fortfahren:

@Test
public void whenWriteMaliciousContent_thenArgumentIllegal() {
    // mocking and initialization

    expect(mockArticleWriter
      .write("easymock",""))
      .andThrow(new IllegalArgumentException());
    replay(mockArticleWriter);

    // write malicious content and capture exception as expectedException

    verify(mockArticleWriter);
    assertEquals(
      IllegalArgumentException.class,
      expectedException.getClass());
}

Im obigen Snippet erwarten wir, dassarticleWriter solide genug ist, umXSS(Cross-site Scripting)-Angriffe zu erkennen.

Wenn der Leser versucht, schädlichen Code in den Artikelinhalt einzufügen, sollte der Verfasser einIllegalArgumentException werfen. Wir haben dieses erwartete Verhalten mitexpect(…).andThrow(…) aufgezeichnet.

6. Verspotten Sie mit Annotation

EasyMock unterstützt auch das Einfügen von Mocks mithilfe von Anmerkungen. To use them, we need to run our unit tests with EasyMockRunner so that it processes @Mock and @TestSubject annotations.

Schreiben wir frühere Schnipsel neu:

@RunWith(EasyMockRunner.class)
public class exampleReaderAnnotatedTest {

    @Mock
    ArticleReader mockArticleReader;

    @TestSubject
    exampleReader exampleReader = new exampleReader();

    @Test
    public void whenReadNext_thenNextArticleRead() {
        expect(mockArticleReader.next()).andReturn(null);
        replay(mockArticleReader);
        exampleReader.readNext();
        verify(mockArticleReader);
    }
}

Entsprechendmock(…) wird ein Schein in Felder eingefügt, die mit@Mock versehen sind. Und diese Mocks werden in Felder der Klasse eingefügt, die mit@TestSubject versehen sind.

Im obigen Snippet haben wir das FeldarticleReader inexampleReader. nicht explizit initialisiert. Wenn wirexampleReader.readNext() aufrufen, können wir das implizitmockArticleReader aufrufen.

Dies lag daran, dassmockArticleReader in das Feld vonthe articleReader injiziert wurde.

Beachten Sie, dass wir die JUnit-TestregelEasyMockRule verwenden können, wenn wir anstelle vonEasyMockRunner einen anderen Testläufer verwenden möchten:

public class exampleReaderAnnotatedWithRuleTest {

    @Rule
    public EasyMockRule mockRule = new EasyMockRule(this);

    //...

    @Test
    public void whenReadNext_thenNextArticleRead(){
        expect(mockArticleReader.next()).andReturn(null);
        replay(mockArticleReader);
        exampleReader.readNext();
        verify(mockArticleReader);
    }

}

7. Verspotten Sie mitEasyMockSupport

Manchmal müssen wir mehrere Mocks in einem einzigen Test einführen, und wir müssen manuell wiederholen:

replay(A);
replay(B);
replay(C);
//...
verify(A);
verify(B);
verify(C);

Das ist hässlich und wir brauchen eine elegante Lösung.

Glücklicherweise haben wir eine KlasseEasyMockSupport in EasyMock, um damit umzugehen. Es isthelps keep track of mocks, such that we can replay and verify them ineine Charge wie diese:

//...
public class exampleReaderMockSupportTest extends EasyMockSupport{

    //...

    @Test
    public void whenReadAndWriteSequencially_thenWorks(){
        expect(mockArticleReader.next()).andReturn(null)
          .times(2).andThrow(new NoSuchElementException());
        expect(mockArticleWriter.write("title", "content"))
          .andReturn("BAEL-201801");
        replayAll();

        // execute read and write operations consecutively

        verifyAll();

        assertEquals(
          NoSuchElementException.class,
          expectedException.getClass());
        assertEquals("BAEL-201801", articleId);
    }

}

Hier verspotteten wir sowohlarticleReader als aucharticleWriter. Wenn Sie diese Mocks auf den Wiedergabemodus setzen, haben wir eine statische MethodereplayAll() verwendet, die vonEasyMockSupport bereitgestellt wird, undverifyAll() verwendet, um ihr Verhalten im Stapel zu überprüfen.

Wir haben auch dietimes(…)-Methode in derexpect-Phase eingeführt. Es hilft anzugeben, wie oft erwartet wird, dass die Methode aufgerufen wird, damit keine doppelten Codes eingefügt werden.

Wir könnenEasyMockSupport auch durch Delegierung verwenden:

EasyMockSupport easyMockSupport = new EasyMockSupport();

@Test
public void whenReadAndWriteSequencially_thenWorks(){
    ArticleReader mockArticleReader = easyMockSupport
      .createMock(ArticleReader.class);
    IArticleWriter mockArticleWriter = easyMockSupport
      .createMock(IArticleWriter.class);
    exampleReader exampleReader = new exampleReader(
      mockArticleReader, mockArticleWriter);

    expect(mockArticleReader.next()).andReturn(null);
    expect(mockArticleWriter.write("title", "content"))
      .andReturn("");
    easyMockSupport.replayAll();

    exampleReader.readNext();
    exampleReader.write("title", "content");

    easyMockSupport.verifyAll();
}

Bisher verwendeten wir statische Methoden oder Anmerkungen, um Mocks zu erstellen und zu verwalten. Unter der Haube werden diese statischen und mit Anmerkungen versehenen Mocks von einer globalenEasyMockSupport-Instanz gesteuert.

Hier haben wir es explizit instanziiert und alle diese Verspottungen durch Delegation unter unsere Kontrolle gebracht. Dies kann helfen, Verwechslungen zu vermeiden, wenn in unserem Testcode Namenskonflikte mit EasyMock auftreten oder ähnliche Fälle vorliegen.

8. Fazit

In diesem Artikel haben wir kurz die grundlegende Verwendung von EasyMock vorgestellt. Hier erfahren Sie, wie Sie Scheinobjekte generieren, ihr Verhalten aufzeichnen und wiedergeben und überprüfen, ob sie sich richtig verhalten.

Falls Sie interessiert sein könnten, sehen Sie sichthis article an, um einen Vergleich von EasyMock, Mocket und JMockit zu erhalten.

Wie immer kann die vollständige Implementierungover on Github gefunden werden.