Hoe RxJava testen?

1. Overzicht

In dit artikel gaan we kijken naar manieren om code te testen die is geschreven met RxJava.

De typische stroom die we creëren met RxJava bestaat uit een Waarneembaar en een Waarnemer. Het waarneembare is een bron van gegevens die een opeenvolging van elementen zijn. Een of meer waarnemers zijn erop geabonneerd om uitgezonden gebeurtenissen te ontvangen.

Doorgaans worden de waarnemer en waarneembare items asynchroon in afzonderlijke threads uitgevoerd, waardoor de code op een traditionele manier moeilijk te testen is.

Gelukkig, RxJava biedt een TestSubscriber class die ons de mogelijkheid geeft om asynchrone, gebeurtenisgestuurde stroom te testen.

2. Het testen van RxJava - de traditionele manier

Laten we beginnen met een voorbeeld - we hebben een reeks letters die we willen zippen met een reeks gehele getallen vanaf 1 inclusief.

Onze test zou moeten bevestigen dat een abonnee die luistert naar gebeurtenissen die worden uitgezonden door gezipte waarneembare letters, gezipte letters met gehele getallen ontvangt.

Zo'n test op een traditionele manier schrijven betekent dat we een lijst met resultaten moeten bijhouden en die lijst moeten bijwerken vanaf een waarnemer. Het toevoegen van elementen aan een lijst met gehele getallen betekent dat onze waarneembare en waarnemers in dezelfde thread moeten werken - ze kunnen niet asynchroon werken.

En dus zouden we een van de grootste voordelen van RxJava missen - het verwerken van evenementen in afzonderlijke threads.

Hier is hoe die beperkte versie van de test eruit zou zien:

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

We verzamelen resultaten van een waarnemer door elementen toe te voegen aan een resultaten lijst. De waarnemer en het waarneembare werken in dezelfde thread, zodat onze bewering correct blokkeert en wacht op een abonneren () methode om te eindigen.

3. Testen van RxJava met behulp van een TestSubscriber

RxJava wordt geleverd met een Test Abonnee class waarmee we tests kunnen schrijven die werken met een asynchrone verwerking van gebeurtenissen. Dit is een normale waarnemer die het waarneembare onderschrijft.

In een test kunnen we de toestand van een TestSubscriber en beweringen doen over die toestand:

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

We passeren een TestSubscriber instantie naar een abonneren () methode op het waarneembare. Dan kunnen we de toestand van deze abonnee bekijken.

TestSubscriber heeft een aantal zeer bruikbare beweringsmethoden die we zullen gebruiken om onze verwachtingen te valideren. De abonnee zou 5 uitgezonden elementen van een waarnemer moeten ontvangen en dat beweren we door de assertValueCount () methode.

We kunnen alle gebeurtenissen onderzoeken die een abonnee heeft ontvangen door het getOnNextEvents () methode.

Bellen met het assertCompleted () methode controleert of een stream waarop de waarnemer is geabonneerd, is voltooid. De assertNoErrors () method beweert dat er geen fouten waren bij het abonneren op een stream.

4. Verwachte uitzonderingen testen

Soms treedt er tijdens onze verwerking een fout op wanneer een waarneembaar gebeurtenissen uitzendt of een waarnemer gebeurtenissen verwerkt. De TestSubscriber heeft een speciale methode om de foutstatus te onderzoeken - de assertError () methode waaraan het type uitzondering als argument moet worden doorgegeven:

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

We creëren het waarneembare dat wordt samengevoegd met een ander waarneembaar met behulp van de concatWith () methode. De tweede waarneembare worpen a RuntimeException terwijl het volgende evenement wordt uitgezonden. We kunnen een type van die uitzondering onderzoeken op een TestSubsciber door het assertError () methode.

De waarnemer die een fout ontvangt, stopt met verwerken en komt in een niet-voltooide toestand terecht. Die staat kan worden gecontroleerd door de assertNotCompleted () methode.

5. Testen op tijd Waarneembaar

Laten we zeggen dat we een Waarneembaar die één gebeurtenis per seconde uitzendt en we willen dat gedrag testen met een TestSubsciber.

We kunnen een tijdgebaseerd definiëren Waarneembaar de ... gebruiken Observable.interval () methode en geef een TimeUnit als argument:

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

De Kruis aan observable zal elke seconde een nieuwe waarde uitzenden.

Aan het begin van een test zijn we op tijd nul, dus onze TestSubscriber zal niet worden voltooid:

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

Om de tijd die verstrijkt in onze test na te bootsen, moeten we een TestScheduler klasse. We kunnen die pas van één seconde simuleren door de advanceTimeBy () methode op een TestScheduler:

scheduler.advanceTimeBy (1, TimeUnit.SECONDS);

De advanceTimeBy () methode zorgt ervoor dat een waarneembare één gebeurtenis oplevert. We kunnen stellen dat één evenement is geproduceerd door een assertValueCount () methode:

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

Onze lijst met brieven bevat 5 elementen, dus als we willen dat een observeerbaar alle gebeurtenissen uitzendt, moeten er 6 seconden verwerking voorbijgaan. Om die 6 seconden na te bootsen, gebruiken we de advanceTimeTo () methode:

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"));

Nadat we de verstreken tijd hebben geëmuleerd, kunnen we beweringen uitvoeren op een TestSubscriber. We kunnen stellen dat alle evenementen zijn geproduceerd door de assertValueCount () methode.

6. Conclusie

In dit artikel hebben we manieren onderzocht om waarnemers en observabelen in RxJava te testen. We hebben gekeken naar een manier om uitgezonden gebeurtenissen, fouten en op tijd gebaseerde waarnemingen te testen.

De implementatie van al deze voorbeelden en codefragmenten is te vinden in het GitHub-project - dit is een Maven-project, dus het moet gemakkelijk te importeren en uit te voeren zijn zoals het is.