Visually test your async code with marble testing: Rx (Java/JS)
The quickie of Alexandre Delattre (Viseo) on Marble testing with Rx (JS/Java/…) during the DevFest Toulouse 2017 was particularly interesting.
What is Rx?
Rx is a library for composing asynchronous and event-based programs by using observable sequences. It provides one core type, the Observable, satellite types (Observer, Schedulers, Subjects) and operators inspired by Array#extras (map, filter, reduce, every, etc) to allow handling asynchronous events as collections.
From RxJS doc
We can use Rx in the frontend (for service calls combinations and reactive user interface) as well as in the backend (micro-services calls combinations, websockets, …).
The current trend is to transform imperative programming into reactive functional programming. With the tools at our disposal, testing asynchronous behaviours is hard, and often, developers just skip this important step. But it is possible! And now, simpler than ever. So how to do that? How to check that our streams unfold the way we want them to?
You guessed right: with Marble Testing.
In order to representObservables, we define Marble diagrams. They are drawn as a horizontal timeline, with events occurring as visual nodes. We can represent them like this example of a the merge function that takes two observables and return a merge of the two.
You can refer to RxMarbles website in order to find interactive diagrams of Rx Observables. In order to use them in code, we define an ASCII notation.
First, we define the time frame (default is 10ms). Then we can have a look at the different symbols that we need:
Example of a mobile weather application
For this example of application, the speaker chose the language Kotlin, but we could do the same with any Rx supported language and platform (see full list on ReactiveX site).
We have an “instant search” application, with the user inputting their city’s name. After a 500ms delay, we launch the search, and a loading progress is visible to the user during the search. Then the result is displayed, or an error, if need be.
Our available interfaces are the following:
Use case diagram
For example, in this diagram, the user starts typing “Toulouse”, and after 500ms without activity (no keystroke pressed), we call the webservice to get the weather in Toulouse. The webservice then returns the response (sunny weather). Afterwards, the user wants to check the weather in Paris, so after the delay, the webservice is called, and then we get the response.
Marble testing implementation
Following are the values that we need in order to test. We map the symbol “0” to the event “empty string”, the symbol “1” to the event the user inputs “tou”, the symbol “t” to the event the user inputs “toulouse”, etc.
And these are the data that the webservice is mocked to respond.
So now, the test look like this.
We obtain an ASCII visual representation of what we simulate the user interaction to be, and then, we tell the test what chain of events we expect to receive from the various observables. In this representation, we can visually check how the different timelines correspond, and easily test that the more complex chains of events actually lead to the observable that we want.
● Tests are more concise and expressive
● Complex cases can be tested visually
● Now testing the global coherence and behaviour is made possible
● The API suffers from differences between the different platforms
● Alignment of marbles can be visually challenging in ASCII
Possible improvements in the future
The speaker concluded by proposing improvements in the future in order to counter the cons:
- Uniformisation of the APIs
- Development of a graphical editor for marbles
He added that if someone in the conference wanted to get involved and develop a graphical editor, it would be great and useful.