Clean Architecture — Kotlin, Dagger 2, RxJava, MVVM and Unit Testing
In this article, we will see how we implement a Clean architecture for our Android application using Dagger 2 and MVVM especially. All code samples are taken from the repo below:
NewAndroidArchitecture - New Android Architecture showing how to use Dagger Android libs
To start the discussion, it might be worth discussing DI. I have used Dagger Android’s libraries to help with DI across the app. Specifically, I am using the
T is a concrete Android class, such as an
Service etc. Only recently did I learn this and yet I feel that it’s power is under-estimated. In a previous incarnation of the repo above, I was using scopes and Components to define application-wide and activity-level dependency objects. (Sigh!)
Let’s look at our
Application class to start us off.
As you can see, we are injecting a
T is the Android
Activity. What does this do? From the documentation it: Performs members-injection on instances of core Android types (e.g. Activity, Fragment). However, I like to think of it in this way. Given that an
Application has many Activities, the
DispatchingAndroidInjector enables objects defined in the
AppModule to be visible to Activity-level modules that will be built later.
AppModule looks like this:
As you can see, I have defined a number of application-wide objects such as
ApiService etc. There is nothing extraordinary of this module. However, it’s corresponding component is quite interesting:
AppComponent above binds the
Application class to the
AppModule. But it does more. It also references the
ActivityBuilder which looks like this:
ActivityBuilder above binds the
MainActivityModule with the
MainActivity. Previously, I remember using custom scopes to confine objects to particular modules, or Android Views (Activities, Fragments) — such as confining a Presenter’s instance to it’s associated Activity. However, the Dagger Android libraries now simplify this to such a degree that we no longer need to provide a Component for the
MainActivityModule or any other activity level Dagger Module . The
MainActivityModule can then provide the
MainActivityViewModel while injecting the objects defined in the
AppModule, such as our Rx
SchedulerProvider, and any objects constructed using constructor injection such as the
Repository instance passed as an argument into the
provideViewModel() function below:
Up to this point, we have provided a framework to inject our View Model into the
MainActivity. Let’s see the
MainActivity code to see how we do this:
Oh wait! That’s strange. Where’s the Dagger Injection code? All that verbose stuff that we used to write. Where’s it gone? It doesn’t exist anymore. Do you remember the
ActivityBuilder defined above — that innocuous abstract class? In there, we bound the
MainActivityModule to the
MainActivity using a
@ContributesAndroidInjector. This provided the link between the module and the Activity. But, how did it inject the
MainActivityViewModel in the Activity code above? Well, the
MainActivity is a subclass of
DaggerActivity which contains one interesting line. Let’s look at the source code of the
AndroidInjection.inject(this); will inject any DI objects referenced by that particular activity — which in this case is the
MainActivity. That means, therefore, that if you do not wish to extend your Activity with
DaggerActivity, you can insert that line in your
But a question still remains. How does the annotation,
@ContributesAndroidInjector, know it can inject the
MainActivityViewModel into the
@ContributesAndroidInjector generates an
T is a concrete Android class (an Activity, Fragment etc.). The interesting aspect about the
AndroidInjector<T> is that it is an interface — its concrete implementation is the
DispatchingAndroidInjector<T> that we saw before. There’s a small line in the docs for this class which reveals how this is achieved:
This class relies on an injected mapping from each concrete class to an
AndroidInjectorof that class. Each concrete class must have its own entry in the map, even if it extends another class which is already present in the map
Now that we have a brief understanding of how we can use Dagger to provide the DI for our app, now we can start talking about MVVM.
Let’s go back to the
MainActivity shown above. We have all our RxJava 2 disposables (or the subscriptions) in the Activity — as held by the
CompositeDisposable. What’s the advantage of this? Coming from a background of using MVP, it’s so easy to forget about clearing our Rx subscriptions and disposing of them. In fact, I even remember this causing a bug in one of my apps. Finding the root cause of that bug was extremely difficult because disposing of Rx subscriptions and how they tie into an Activity’s lifecycle is not something we automatically think of when using MVP — we tend to hold the subscriptions (
CompositeDisposable) in the Presenter, or a BasePresenter, and this can blind us to the concept that Rx subscriptions should be tied to the lifecycle of the Android view class. In my view, this is one of the most valuable benefits of MVVM over MVP — not from a code point of view, but from a developer’s view of the code — it reminds us of what’s required.
We can now take a closer look at the ViewModel:
As you can see, the key responsibilities of the ViewModel is to provide data from a Repository and enforce Rx Scheduling. The ViewModel has no reference to the Activity or Android view which means we reduce the likelihood of memory leaks.
However, did you notice that the ViewModel does not interact with the
ApiService directly — but instead interacts with the
Repository class. The
Repository class contains a dependency to the
ApiService as shown below:
So why have we inserted an extra layer, the Repository layer, in between the
ApiService and the ViewModel? Even though the
Repository class in this case simply passes the data from the api to the ViewModel, there could be a requirement to store the data into some form of storage. Now we need some logic to determine whether to retrieve data from the API or from local storage and this is a separate responsibility of concern. Having a clear separation of concerns is a key pillar of maintaining Clean Architecture. The ViewModel’s key responsibility is to expose states for the View to consume to ensure the View receives the latest data. The ViewModel’s concern is not to decide where the data comes from; that’s the concern of the Repository class(es).
The following diagram summarises what we have learned so far. The arrows representing the direction of the flow of data.
Let’s see how we can now unit test our ViewModel using Mockito.
One of the biggest issues we faced as a team with the introduction of Kotlin was unit testing. We had unit tests for all our presenters and that was great because last year we felt we had achieved a test coverage that we were pleased with — even though everything wasn’t unit tested, but the fact that all our presenters had unit tests meant that we could be fairly comfortable knowing that our core logic was unit tested. However, the introduction of Kotlin this year changed that — suddenly, we were starting a process of removing unit tests because they all used Mockito and as we know Mockito does not support mocking final classes. Eventually, it got to such a point that we knew something had to change. All the work we achieved in 2016 was being undone by the introduction of Kotlin and that isn’t right.
So a little more research into Mockito and we found this dependency that allows us mock final classes (without using PowerMockito).
The Inline Mockito dependency allows us to mock final classes and methods. Even though there are a few issues with it, as it’s still being developed, it generally worked very well for us. It means we can write our unit tests in the same way as we have done before. Let’s see the unit test for our
Our unit tests look quite familiar — we wouldn’t automatically deduce that we are using the Mockito Inline library rather than the standard Mockito library. This is what makes the inline library powerful. We created a mock of our dependency to the ViewModel — the
Repository is mocked using the
@Mock annotation. We then setup the Rx schedulers to use the main thread of the background and foreground schedulers. Once that’s done, we can create an instance of the class under test (the
As the ViewModel returns
Observables, or in this case a
Single, we need a means of subscribing to that
Single emitted while carrying out our tests. That is where our
TestObserver comes in. It allows us to subscribe to the
Single<IpAddress> emitted by the ViewModel. We can assert that there were no errors and we can assert that the
IpAddress object emitted is the expected outcome. We can unit test our
Repository layer code in a similar manner too.
In conclusion, I hope I have been able to impart some of the caveats in developing a Clean Architecture using Kotlin, Dagger 2, RxJava and the MVVM pattern that is testable. Keep coding!!