Comment tester RxJava?

1. Vue d'ensemble

Dans cet article, nous examinerons les moyens de tester le code écrit à l'aide de RxJava.

Le flux typique que nous créons avec RxJava se compose d'un observable et d'un observateur. L'observable est une source de données qui est une séquence d'éléments. Un ou plusieurs observateurs s'y abonnent pour recevoir les événements émis.

En règle générale, l'observateur et les observables sont exécutés dans des threads séparés de manière asynchrone, ce qui rend le code difficile à tester de manière traditionnelle.

Heureusement, RxJava fournit une classe TestSubscriber qui nous donne la possibilité de tester des flux asynchrones et pilotés par les événements.

2. Tester RxJava - la manière traditionnelle

Commençons par un exemple - nous avons une séquence de lettres que nous voulons compresser avec une séquence d'entiers à partir de 1 inclus.

Notre test doit affirmer qu'un abonné qui écoute les événements émis par l'observable zippé reçoit des lettres zippées avec des entiers.

Ecrire un tel test de manière traditionnelle signifie que nous devons conserver une liste de résultats et mettre à jour cette liste par un observateur. L'ajout d'éléments à une liste d'entiers signifie que notre observable et nos observateurs doivent travailler dans le même thread - ils ne peuvent pas fonctionner de manière asynchrone.

Et donc, nous manquerions l'un des plus grands avantages de RxJava - le traitement des événements dans des threads séparés.

Voici à quoi ressemblerait cette version limitée du test:

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

Nous agrégons les résultats d'un observateur en ajoutant des éléments à une liste de résultats . L'observateur et l'observable travaillent dans le même thread, donc notre assertion se bloque correctement et attend la fin d'une méthode subscribe () .

3. Test de RxJava à l'aide d'un TestSubscriber

RxJava est livré avec une classe TestSubsriber qui nous permet d'écrire des tests qui fonctionnent avec un traitement asynchrone des événements. C'est un observateur normal qui souscrit à l'observable.

Dans un test, nous pouvons examiner l'état d'un TestSubscriber et faire des affirmations sur cet état:

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

Nous transmettons une instance de TestSubscriber à une méthode subscribe () sur l'observable. Ensuite, nous pouvons examiner l'état de cet abonné.

TestSubscriber a des méthodes d'assertion très utiles que nous utiliserons pour valider nos attentes. L'abonné doit recevoir 5 éléments émis par un observateur et nous l'affirmons en appelant laméthode assertValueCount () .

Nous pouvons examiner tous les événements qu'un abonné a reçus en appelant la méthode getOnNextEvents () .

L'appel de la méthode assertCompleted () vérifie si un flux auquel l'observateur est abonné est terminé. La méthode assertNoErrors () affirme qu'il n'y a eu aucune erreur lors de l'abonnement à un flux.

4. Test des exceptions attendues

Parfois, dans notre traitement, lorsqu'une observable émet des événements ou qu'un observateur traite des événements, une erreur se produit. Le TestSubscriber a une méthode spéciale pour examiner l'état d'erreur - la méthode assertError () qui prend le type d'une exception comme argument:

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

Nous créons l'observable qui est jointe à une autre observable en utilisant la méthode concatWith () . Le deuxième observable lève une RuntimeException lors de l'émission de l'événement suivant. Nous pouvons examiner un type de cette exception sur un TestSubsciber en appelant la méthode assertError () .

L'observateur qui reçoit une erreur cesse le traitement et se retrouve dans un état non terminé. Cet état peut être vérifié par la méthode assertNotCompleted () .

5. Test d' observable basé sur le temps

Disons que nous avons un Observable qui émet un événement par seconde et que nous voulons tester ce comportement avec un TestSubsciber .

Nous pouvons définir un Observable basé sur le temps en utilisant la méthode Observable.interval () et passer un TimeUnit comme argument:

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

Le tick observable émettra une nouvelle valeur toutes les secondes.

Au début d'un test, nous sommes au temps zéro, donc notre TestSubscriber ne sera pas terminé:

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

Pour émuler le temps qui passe dans notre test, nous devons utiliser une classe TestScheduler . Nous pouvons simuler cette passe d'une seconde en appelant la méthode advanceTimeBy () sur un TestScheduler :

scheduler.advanceTimeBy(1, TimeUnit.SECONDS);

La méthode advanceTimeBy () fera en sorte qu'un observable produise un événement. Nous pouvons affirmer qu'un événement a été produit en appelant une méthode assertValueCount () :

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

Notre liste de lettres contient 5 éléments, donc lorsque nous voulons que l'observable émette tous les événements, 6 secondes de traitement doivent s'écouler. Pour émuler ces 6 secondes, nous utilisons la méthode advanceTimeTo () :

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

Après avoir émulé le temps passé, nous pouvons exécuter des assertions sur un TestSubscriber . Nous pouvons affirmer que tous les événements ont été produits en appelant la méthode assertValueCount () .

6. Conclusion

Dans cet article, nous avons examiné les moyens de tester les observateurs et les observables dans RxJava. Nous avons examiné un moyen de tester les événements émis, les erreurs et les observables temporels.

L'implémentation de tous ces exemples et extraits de code se trouve dans le projet GitHub - il s'agit d'un projet Maven, il devrait donc être facile à importer et à exécuter tel quel.