Wie teste ich RxJava?

1. Übersicht

In diesem Artikel werden Möglichkeiten zum Testen von Code untersucht, der mit RxJava geschrieben wurde.

Der typische Fluss, den wir mit RxJava erstellen, besteht aus einem Observable und einem Observer. Das Observable ist eine Quelle für Daten, die eine Folge von Elementen sind. Ein oder mehrere Beobachter abonnieren es, um gesendete Ereignisse zu empfangen.

In der Regel werden Observer und Observables asynchron in separaten Threads ausgeführt. Dies macht es schwierig, den Code auf herkömmliche Weise zu testen.

Glücklicherweise bietet RxJava eine TestSubscriber- Klasse, mit der wir den asynchronen, ereignisgesteuerten Ablauf testen können.

2. Testen von RxJava - auf traditionelle Weise

Beginnen wir mit einem Beispiel - wir haben eine Folge von Buchstaben, die wir mit einer Folge von ganzen Zahlen von 1 einschließlich schließen wollen.

Unser Test sollte bestätigen, dass ein Abonnent, der Ereignisse abhört, die von gezippten Observable ausgegeben werden, Buchstaben erhält, die mit ganzen Zahlen gezippt sind.

Wenn wir einen solchen Test auf herkömmliche Weise schreiben, müssen wir eine Ergebnisliste führen und diese Liste von einem Beobachter aktualisieren. Das Hinzufügen von Elementen zu einer Liste von Ganzzahlen bedeutet, dass unsere Observable und Observer im selben Thread arbeiten müssen - sie können nicht asynchron arbeiten.

Und so würde uns einer der größten Vorteile von RxJava fehlen - die Verarbeitung von Ereignissen in separaten Threads.

So würde diese eingeschränkte Version des Tests aussehen:

List letters = Arrays.asList("A", "B", "C", "D", "E"); List results = new ArrayList(); Observable observable = Observable .from(letters) .zipWith( Observable.range(1, Integer.MAX_VALUE), (string, index) -> index + "-" + string); observable.subscribe(results::add); assertThat(results, notNullValue()); assertThat(results, hasSize(5)); assertThat(results, hasItems("1-A", "2-B", "3-C", "4-D", "5-E"));

Wir aggregieren die Ergebnisse eines Beobachters, indem wir einer Ergebnisliste Elemente hinzufügen . Der Beobachter und das Beobachtbare arbeiten im selben Thread, sodass unsere Behauptung ordnungsgemäß blockiert und auf den Abschluss einer subscribe () -Methode wartet .

3. Testen von RxJava mit einem TestSubscriber

RxJava wird mit einer TestSubsriber- Klasse geliefert , mit der wir Tests schreiben können, die mit einer asynchronen Verarbeitung von Ereignissen arbeiten. Dies ist ein normaler Beobachter, der das Beobachtbare abonniert.

In einem Test können wir den Status eines TestSubscriber untersuchen und Aussagen zu diesem Status machen:

List letters = Arrays.asList("A", "B", "C", "D", "E"); TestSubscriber subscriber = new TestSubscriber(); Observable observable = Observable .from(letters) .zipWith( Observable.range(1, Integer.MAX_VALUE), ((string, index) -> index + "-" + string)); observable.subscribe(subscriber); subscriber.assertCompleted(); subscriber.assertNoErrors(); subscriber.assertValueCount(5); assertThat( subscriber.getOnNextEvents(), hasItems("1-A", "2-B", "3-C", "4-D", "5-E"));

Wir übergeben eine TestSubscriber- Instanz an eine subscribe () -Methode auf dem Observable. Dann können wir den Status dieses Teilnehmers untersuchen.

TestSubscriber verfügt über einige sehr nützliche Assertionsmethoden, mit denen wir unsere Erwartungen überprüfen . Der Abonnent sollte 5 emittierte Elemente von einem Beobachter erhalten, und wir bestätigen dies durch Aufrufen der assertValueCount () -Methode.

Wir können alle Ereignisse untersuchen, die ein Abonnent durch Aufrufen der Methode getOnNextEvents () erhalten hat .

Durch Aufrufen der Methode assertCompleted () wird überprüft, ob ein Stream, den der Beobachter abonniert hat, abgeschlossen ist. Die assertNoErrors () -Methode bestätigt, dass beim Abonnieren eines Streams keine Fehler aufgetreten sind .

4. Testen erwarteter Ausnahmen

Manchmal tritt in unserer Verarbeitung ein Fehler auf, wenn ein Observable Ereignisse aussendet oder ein Beobachter Ereignisse verarbeitet. Der TestSubscriber verfügt über eine spezielle Methode zum Überprüfen des Fehlerstatus - die assertError () -Methode, die den Typ einer Ausnahme als Argument verwendet:

List letters = Arrays.asList("A", "B", "C", "D", "E"); TestSubscriber subscriber = new TestSubscriber(); Observable observable = Observable .from(letters) .zipWith(Observable.range(1, Integer.MAX_VALUE), ((string, index) -> index + "-" + string)) .concatWith(Observable.error(new RuntimeException("error in Observable"))); observable.subscribe(subscriber); subscriber.assertError(RuntimeException.class); subscriber.assertNotCompleted();

Wir erstellen das Observable, das mit einem anderen Observable verbunden ist, mithilfe der concatWith () -Methode. Das zweite Observable löst eine RuntimeException aus, während das nächste Ereignis ausgegeben wird . Wir können einen Typ dieser Ausnahme in einem TestSubsciber untersuchen, indem wir die assertError () -Methode aufrufen .

Der Beobachter, der einen Fehler empfängt, beendet die Verarbeitung und befindet sich in einem nicht abgeschlossenen Zustand. Dieser Status kann mit der Methode assertNotCompleted () überprüft werden .

5. Testen der zeitbasierten Beobachtung

Angenommen , wir haben ein Observable , das ein Ereignis pro Sekunde ausgibt, und wir möchten dieses Verhalten mit einem TestSubsciber testen .

Wir können ein zeitbasiertes Observable mit der Observable.interval () -Methode definieren und eine TimeUnit als Argument übergeben:

List letters = Arrays.asList("A", "B", "C", "D", "E"); TestScheduler scheduler = new TestScheduler(); TestSubscriber subscriber = new TestSubscriber(); Observable tick = Observable.interval(1, TimeUnit.SECONDS, scheduler); Observable observable = Observable.from(letters) .zipWith(tick, (string, index) -> index + "-" + string); observable.subscribeOn(scheduler) .subscribe(subscriber);

Das beobachtbare Häkchen gibt jede Sekunde einen neuen Wert aus.

Zu Beginn eines Tests befinden wir uns zum Zeitpunkt Null, sodass unser TestSubscriber nicht abgeschlossen wird:

subscriber.assertNoValues(); subscriber.assertNotCompleted();

Um die in unserem Test verstrichene Zeit zu emulieren, müssen wir eine TestScheduler- Klasse verwenden. Wir können , dass ein zweiter Durchlauf simulieren , indem Sie den Aufruf advanceTimeBy () Methode auf einem TestScheduler :

scheduler.advanceTimeBy(1, TimeUnit.SECONDS);

Mit der Methode advancedTimeBy () wird ein beobachtbares Ereignis erzeugt. Wir können behaupten, dass ein Ereignis durch Aufrufen einer assertValueCount () -Methode erzeugt wurde:

subscriber.assertNoErrors(); subscriber.assertValueCount(1); subscriber.assertValues("0-A");

Unsere Liste von Buchstaben enthält 5 Elemente. Wenn also ein Observable alle Ereignisse ausgeben soll, müssen 6 Sekunden der Verarbeitung vergehen. Um diese 6 Sekunden zu emulieren, verwenden wir die Methode advancedTimeTo () :

scheduler.advanceTimeTo(6, TimeUnit.SECONDS); subscriber.assertCompleted(); subscriber.assertNoErrors(); subscriber.assertValueCount(5); assertThat(subscriber.getOnNextEvents(), hasItems("0-A", "1-B", "2-C", "3-D", "4-E"));

Nach dem Emulieren der verstrichenen Zeit können wir Assertions auf einem TestSubscriber ausführen . Wir können behaupten, dass alle Ereignisse durch Aufrufen der assertValueCount () -Methode erzeugt wurden.

6. Fazit

In diesem Artikel haben wir Möglichkeiten zum Testen von Beobachtern und Observablen in RxJava untersucht. Wir haben uns eine Möglichkeit angesehen, emittierte Ereignisse, Fehler und zeitbasierte Observablen zu testen.

Die Implementierung all dieser Beispiele und Codefragmente finden Sie im GitHub-Projekt - dies ist ein Maven-Projekt, daher sollte es einfach zu importieren und auszuführen sein, wie es ist.