Abstract & Test Rendering Logic of State in Android

Ankur Gupta
ProAndroidDev
Published in
4 min readJan 4, 2021

--

State is a crucial part of any frontend application. We do not want the UI to end up in an un-intentional, glitched, or lose state, no matter what the user does. It is where predictable states come into the picture.

UI states should be predictable and thoroughly tested. By predictable I mean you should plan out all the scenarios the UI can show for a screen and represent all the data in a class. For example, a screen that loads a list of movies from the network can have these possible states:

  1. Loading
  2. Success : But without any movies(empty)
  3. Success : With a list of movies
  4. Error

Problem

For such a simple use case we already have four states. Imagine, if you need to fetch a list of actors on the same screen or what if it is offline first app and now you have to fetch from database first and lazily load from network? These states multiply over time as requirements starts to grow.

If you are not writing tests to check the correctness of rendering logic of the state, they become more error prone. Tests will also save your time from manually testing each scenario especially when the navigation to particular screen is buried deep under multiple steps.

And also, why should the View(Activity) contain any rendering code? View should be dumb and its job should be*just* to bind the data with XML.

Solution

In this tutorial I will demonstrate how you can abstract out the logic of rendering state with the help of interfaces and also test it. For this we will build an app which fetches a greeting from network then shows it to user. I’ll be covering three states — Loading, Success and Error.

This article assumes your business logic/architecture produces a single state for view layer which represents all the data required to render the UI. If you are not sure about it and how to do that I recommend you to read this article first — Single view states

This is the state class we’ll be writing tests for —

And also let’s add an empty interface that’ll render our state —

We will complete the render() method by following Test Driven Development process(TDD). In short, we will write the test first then make it fail then write production code to make the test pass. That’s it! So let’s start with first tests.

  1. Add the nhaarman mockito dependency to your android project.
  2. The first thing that’ll happen when a user opens the app is, it will fetch the greeting message from the network. For this scenario, we want to show a progress bar while the network request complete. We’ll add the blank test and name it accordingly.

3. The three parts of the tests are Setup, Act, and Assert.

Assert: We will start with writing assert first because we know what result we are expecting. Then we will build our act and setup based on that. So just think there is an object of something and let’s call it view. For this test, we will verify a method inside view named showProgress(bool) is called with true argument.

Act: It is usually the call to the function we are testing. In this case we are testing whether render() function could render a state with fetchStatus as Loading.

Setup: This is where we define what view is and what is the prescribed data. In this case view is instance of interface GreetingView. Since Mockito can’t verify method calls through an interface so we’ll have to implement it to empty class. Let’s call it SpyableGreetingView and we’ll make sure we annotate it with Spy and class is open.

4. On running the test it fails with Unresolved reference: showProgress, because we do not have any method as showProgress(bool). Let’s fix the test by adding bare minimum code in the GreetingView just to make the test pass.

The test passes now. It means that our view layer is now capable of rendering the Loading state.

Yay! We’ve completed the first scenario.

Now let’s take Error scenario, if there is any kind of exception while fetching we need to stop showing progress bar and then show the relevant error message to the user.

Again, we will write the test first. Make it fail. Now we have to correct the render() function such that our previous test also passes. The condition is quite simple in this case so we’ll just use the when expression.

The process would be the same for the Success scenario also. I’m skipping it here for the sake of the length of this tutorial. But you can check the code in the repository.

Our final interface with rendering logic will look like this.

All we have to do now is to implement this interface on a actual platform view like Activity, Fragment or even RecyclerView’s ViewHolder. Then call the render() function on the state emitted by model/view model. And we are done!

Conclusion

You can see how Activity is super-dumb now. All the rendering logic has been abstracted away in a plain Kotlin interface, which is easy to read, easy to refactor, maintain, can be shared across multiple views, and is also well tested. And you saved a lot of time spent in manual testing.

Complete code is available in the GitHub repository linked below. I would also highly recommend viewing the code commit-by-commit if you are new to TDD.

What do you think?

What do you think about this solution? I would love to hear your ideas :)

Please, feel free reach out to me on Twitter or LinkedIn

--

--

SDE-2 (Android) at @Hapramp | TDD | Rx | Architecture | Problem Solving