Letzte Woche habe ich ein neues Test-Bundle zu Spring Integration hinzugefügt. Dieses Bundle enthält Matcher, die das Schreiben von Tests für Spring Integration-Konfigurationen erleichtern. Es baut auf JUnit 4, Hamcrest und Mockito auf. Auch wenn Sie kein Benutzer von Spring Integration sind, werde ich dafür sorgen, dass dieser Beitrag Ihnen ein paar Ideen für die Verwendung von Matchern gibt, um besser lesbare Tests zu schreiben.Zunächst ein paar Hinweise zu Spring Integration, um Ihnen etwas Hintergrundwissen zu vermitteln.Spring Integration ist ein Framework, das darauf abzielt, Enterprise Integration Patterns zu implementieren, wie sie in dem Buch von Hohpe und Woolf beschrieben sind. Das Buch ist sehr schön, aber die Verwendung dieser Muster erfordert einige Boilerplate und ein Plumbing, das gut zu einem Framework passt, daher Spring Integration. JUnit 4 sollten Sie bereits kennen, aber eine kürzlich hinzugefügte Funktion verdient eine weitere Erwähnung. In den neuesten Versionen von JUnit können Sie die Funktion assertThat(T actual, Matcher< T > matcher) verwenden, die auf Hamcrest-Matchern aufbaut. Dies macht Ihre Assertions bereits viel lesbarer, aber in Kombination mit Mockito werden die Testfälle zur Poesie. Ich gehe davon aus, dass ich Ihnen das alles nicht erklären muss, aber wenn Sie keine Ahnung haben, wovon ich spreche, sollten Sie sich wirklich erkundigen.
EIP beinhaltet häufig asynchrone Übergaben, und es beinhaltet auch die Verpackung von Objektnutzlasten in Nachrichten. Beides kann beim Schreiben von Tests hinderlich sein. Zum Glück können wir diese Boilerplate leicht abstrahieren. Heute habe ich die Lösung für das Unwrapping-Problem vorgestellt, über die Lösung für das zweite Problem werde ich ein anderes Mal sprechen.Das Unwrapping von Nachrichten erfordert einige Boilerplate, die den Test schwerer lesbar macht. Der folgende Ausschnitt aus den AggregatorEndpointTests von Spring Integration zeigt deutlich beide Probleme.
[source lang="java"]
@Test
public void testCompleteGroupWithinTimeoutWithSameId() throws Exception {
//...
Nachricht<?> reply = replyChannel.receive(500);
assertNotNull(reply);
assertEquals("123456789", reply.getPayload());
[/source] }
In wenigen Sekunden ist klar, was wir hier zu tun versuchen, aber es sieht nicht sehr elegant aus. Mit dem neuen Testprojekt wollen wir diese Art von Code besser lesbar machen. Zu diesem Zweck haben wir einige Hilfsklassen erstellt.
Zunächst gibt es die Klassen HeaderMatcher und PayloadMatcher. Mit ihnen können Sie Dinge tun wie:
[source lang="java"]
import static ...HeaderMatcher.*;
@Test
public void testCompleteGroupWithinTimeoutWithSameId() throws Exception {
//...
Nachricht<?> reply = replyChannel.receive(500);
assertThat(reply, hasPayload(is(String.class))); //Übergabe eines Matchers
assertThat(reply, hasPayload("123456789"))); //auf Basis der Gleichheitsprüfung
[/source] }
Der HeaderMatcher erzeugt Matcher, die die Kopfzeilen einer Nachricht auf ähnliche Weise untersuchen.
Dies wird durch ein wenig Mockito-Magie mit den MockitoMessageMatchern abgerundet:
[source lang="java"]
@Test
public void anyMatcher_withWhenArgumentMatcherAndEqualPayload_matching() throws Exception {
when(channel.send(messageWithPayload(SOME_PAYLOAD))).thenReturn(true);
assertThat(channel.send(message), is(true));
[/source] }
Ich bin sehr zufrieden mit der Eleganz dieser Vorgehensweise und denke, dass diese Techniken in jeder Domäne sehr nützlich sind. Wenn Sie sich ein wenig mehr Zeit nehmen, um Matcher-Utilities zu erstellen, die tatsächlich zu Ihrer Domäne passen, wird sich dies bei der Wartung von Testfällen auszahlen. Wann immer Sie sich dabei ertappen, dass Sie argThat(..) in Mockito häufig verwenden oder über BaseMatcher-Implementierungen stolpern, denken Sie an diesen Blog.Ich hoffe, Sie haben dieses Beispiel als nützlich empfunden. Wenn Sie sich die Details des Spring Integration Testprojekts ansehen möchten, können Sie sich die Commits zu diesem Thema ansehen. Wenn Sie etwas Verrücktes entdeckt haben, sollten Sie mir oder einem anderen Mitglied des Spring Integration Teams dies vor der nächsten Version mitteilen.
Zu guter Letzt: Ein großes Lob an Alexander Peters, der einen Patch für dieses Problem geschrieben hat.