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!

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.