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:
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.
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.
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.