[λ] thegeez blog index

Testing RxJava with executable marble diagrams

25 Mar 2013

Reactive Extensions (Rx) from the .Net world are observable sequences. In particular they are push based sequences, in contrast to the pull based seq api in Clojure. Furthermore you can subscribe to and unsubscribe from observable sequences. This also makes observable sequences a nice candidate to replace futures and callbacks in asynchronous code.

For more information see any of the following resources:

In particular the "Intro to Rx" book is a great resource. Every other example will have you wishing to be able to use Rx in Clojure. Luckily Netflix recently open sourced their Java variation of Rx called RxJava.


Marble diagrams

Both the Intro to Rx book and the RxJava javadoc use the concept of marble diagrams. A marble diagram shows how different operators work and how sequences can be combined. The following diagram is the example from RxJava: Source: Neflix/RxJava

The following is the same diagram in Clojure code:

(deftest flip-example
  (is (marble [:src [ 1  2  3 4 5 6 | ]
               :expr (.map :src (fn [value]
                                  (if (= value 4)
                                       (throw (Exception. "DemoFail on 4"))
                                       (* -1 value))))
               :out [-1 -2 -3 X]])))
Source: test/reactive_marbles/rxjava_test.clj

The benefit of the diagram being in code is that it can be executed. The reactive-marbles project contains most of the example marble diagrams from RxJava in Clojure, as code and executable as tests.

Found bugs

Running reactive-marbles gives the following output:

lein test reactive-marbles.rxjava-test

FAIL in (take-test) (rxjava_test.clj:139)
Marble diagram test
[:src [1 2 3 4 5 |]
 :expr (Observable/take :src 2)
 :out [1 2 |]]
expected: [1 2 |]
  actual: [1 2 -]

FAIL in (to-sorted-list) (rxjava_test.clj:163)
Marble diagram test
[:src [2 5 1 6 3 4 |]
 :expr (Observable/toSortedList :src)
 :out [- - - - - - [1 2 3 4 5 6] |]]
expected: [- - - - - - [1 2 3 4 5 6] |]
  actual: [- - - - - - [1 1 2 2 3 3 4 4 5 5 6 6] |]

FAIL in (list-test) (rxjava_test.clj:153)
Marble diagram test
[:src [1 2 3 4 5 6 |]
 :expr (Observable/toList :src)
 :out [- - - - - - [1 2 3 4 5 6] |]]
expected: [- - - - - - [1 2 3 4 5 6] |]
  actual: [- - - - - - [1 1 2 2 3 3 4 4 5 5 6 6] |]

FAIL in (take-while-test) (rxjava_test.clj:145)
Marble diagram test
[:src [1 2 3 4 5 |]
 :expr (Observable/takeWhile :src (fn [i] (< i 3)))
 :out [1 2 |]]
expected: [1 2 |]
  actual: [1 2 -]

Ran 33 tests containing 33 assertions.
4 failures, 0 errors.
Tests failed.

The 4 failed test cases are the result of 2 bugs in RxJava 0.6.1. See rxjava_test.clj for details on the found bugs and links to the bug reports.

Marble diagrams are useful to see the difference between operators and to get an intuition about their behavior. As code they serve as both documentation and tests. However marble diagrams do not properly show the importance of subscriptions and how observable sequences behave when they produce values at variable rates. Again, the Intro to Rx book is a great resource to learn about that.

Code is on github: thegeez/reactive-marbles