Metronome: A Fully Unit-Tested Rx-Based Reference Application

Several months ago, as a way to get myself up to speed with using and unit-testing RxJava, I developed and posted this reference application. I had ambitions of immediately following it up with a blog post, but I got distracted and I never got around to sharing it with anyone. So better late than never I suppose.

Use Case

The application is that of a metronome with the following features:

  • A core metronome “engine” which triggers “beat” events on a timed interval. The engine exposes methods for starting and stopping the metronome, as well as a method for changing the tempo (i.e. the time between beats).
  • A user interface for starting and stopping the metronome, and for making changes to the tempo. The tempo-setting interface is implemented via screen taps. As the use taps an area the screen, the time between the taps is captured and used to set the tempo.
  • An audio service that plays a clicking sound on each beat. This is implemented as an Android Service so that the audio can continue playing even if the UI is closed.

Implementation and Testing Details

I won’t cover every aspect of how I implemented the features of the application, but I think it is worth highlighting a few details:

  • I used Hugo Visser’s android-apt plugin because I found that the built-in Gradle “provided” functionality didn’t reliably do the apt code-generation for Dagger 2. Dagger 1 worked fine for me without this plug-in, but now that I have transitioned to Dagger 2, android-apt is a must.
  • I used Jake Warton’s RxBinding library for observing click events.
  • To get the time between screen taps, I used the (very handy) built-in timeInterval Rx operator.
  • I wrote a number of Func1 classes for filtering and mapping events. There is a filter that is chained to the events emmitted by timeInterval Rx operator so that only events within a certain range of values are emitted. This restricts the range of tempos that are ultimately set. Also I have a number of mapping classes for doing things like rounding and converting from milliseconds to beats-per-minute (BPM). While these classes are simple and could easily have been provided in-line/anonymously, I chose to break them out into separate classes. This allowed me to easily unit-test each one of them, and makes them available for reuse in another project should I need them.
  • I use Dagger 2 to inject all of my dependencies. Most notably, I inject separate @Named Rx Schedulers for subscribing/observing on various threads. Doing so allowed me to override these schedulers in my unit tests with TestScheduler to properly test the behavior of timed and/or asynchronous code without any deferred execution, Thread.sleep(), etc.
  • To properly test the Activity and Service classes I used Robolectric. More specifically, I made use of the relatively-new ActivityController and ServiceController classes to control the Activity/Service lifecyles. This allowed me to have these classes get properly initialized and Dagger-injected (in their respective onCreate methods), but still able to override their dependencies as needed before the subsequent lifecycle events such as onStart were called.
  • I used Mockito to generate mocked dependencies and AssertJ for writing assertions, because… well… they’re great.
  • (Edit Jan 19, 2016) Thanks to a pull request from R. Toledo, Jacoco test-coverage reports are now available via the jacocoTestReport Gradle task.

Summary

The source code of the project is available here: https://github.com/wongcain/metronome-android/

Also, here is a list of the tools/libraries that I used:

I hope that this post and the sample application are helpful. Feel free to ask me any questions or offer any suggestions. Cheers!