Unit Testing Components
I was really excited when I realized I can break down my app’s code into smaller intuitive components, rather than separating the “business logic” from the framework code: Custom Views as Components. I told everyone at work and developers outside work about my newfound treasure. I felt this could actually be a viable architecture, right up there with MVVM and MVI.
But then it hit me: How can one write unit tests for components?
In order to be a viable architecture, it had to be unit testable.
After some soul searching (read: googling), I became confident of what I my gut was telling me all along:
components are independent units. They should be unit testable.
We have an awesome tool called Robolectric, that makes it possible to write fast tests for code that calls the android framework code. Some of you might have cringed at the fact that I will use robolectric and tightly “couple” my code to a particular framework thereby violate Uncle Bob’s clean architecture. To you folks I say, “Clean architecture is not gospel”; it’s just one way to architect code.
After philosophy comes code
Rather than showing bits and pieces of a really large app, I decided to implement The TODO app- google’s recommended way demonstrating an android architecture.
The Android Architecture Blueprints project demonstrates strategies to help solve or avoid these common problems (of missing architectures). This project implements the same app using different architectural concepts and tools.
I cloned the repo and created a new branch from todo-mvp-rxjava
. You can follow along the commits in the branch and see the evolution of the code.
First failing test
In my first commit, I setup robolectric, created an empty (i.e. non-functional) component and, in the spirit of TDD (Test Driven Development), I wrote my first failing unit test:
Naturally, the test failed.

So I fixed it in the next commit- Make `empty title should show error` test pass. Now my component class looks like this:
Nothing more. In TDD, we write just enough code to make our tests pass:

Save task to Repository
Showing errors is trivial, saving to repository is not.
To keep my components testable, I needed to provide my components with a mock version of the TasksRepository
. Maybe, I need some kind of dependency injection (DI). But if I used DI, how could I add components in XML layouts? I don’t want to give that up just so that I could test my components. I didn’t want to go back to the unnecessary abstractions hell that I had faced in MVVM and MVI:
interface AddEditView {
😱🔫
In the todo-mvp-rxjava
version, the repos were singletons. This is an anti-pattern.
I knew I wanted to tie the repo object to the android.app.Application
. I could make MyApplication
a dependency provider and any component could pull out dependencies (as opposed to have injected dependencies). I struck gold, when I found out Context#getApplicationContext
(sneakily) returns the Application
object.
So I created 2 Application
classes: MyApplication
and MockApplication
I added 1 to the AndroidManifest.xml
and the other to robolectric.properties
. Now, along with the kotlin magic (kotlin extension functions), I can call context.getTasksRepository()
on any context object. In my view I call context.getTasksRepository().saveTask(task)
Now my (failing) test looks like this:
Making the test pass was easy.
Finish the Activity
How should one implement the behaviour of finishing the activity? We have to call Activity#finish()
but where should we put this code? We could implement a callback in the view and let the Activity
call finish()
inside it. But so far, the activity has no idea what the AddEditRootView
does. Let’s not ruin that.
Instead, I say, we should access the activity object from inside the view without the activity even knowing it. In any view, the context passed in is always the activity
. This is true for views put in XML, created by Fragment
s or RecyclerView
s. Use this fact! In kotlin
it would look like:
(context as Activity).apply {
setResult(Activity.RESULT_OK)
finish()
}
Managing State
By now we have completed the “Add Task” feature. Now for the “Edit Task”. The component needs to show the existing task. It also needs the taskId
so that the task can be saved. This brings us to the important question: How to manage state in components?
View
components are not meant to save state. Classes in the android.widget
package are meant to display data while those in android.app
package (Activity, Fragment
) are meant to manipulate the data. The View
component is a widget.
For real applications very rarely have I come across the need for a transient state. Either the state is persistent, in which case it’ll be needed to backed by the network and/or local storage, or there is no need to store state at all. Only in cases like, if you’re displaying a map you may want to store the current lat-lng and zoom level transiently. In such cases use a fragment.
Robolectric
It is a fact that robolectric tests take longer to run. They take a couple of seconds longer than JUnit
but they don’t take so long that you’ll not run them altogether.
Using robolectric will reduce the number of espresso tests.
Running the longer robolectric tests is still much faster than creating unnecessary abstractions that you would otherwise need to keep the android framework outside of your unit tests. See DHH’s Test-induced design damage.
It was a long road but I finally ported the AddEditTaskAcivity
to the component architecture: link to the github project.
Along the way I created some DSLs (rules and hacks) specific to the component architecture. Every architecture needs them. But these DSLs are surprisingly lesser in number than what you‘d have to do in MVVM or MVI. This means that when you hire new team members, you don’t have to assess their MV* prowess or, even worse, take a couple of weeks to train them.