Unit testing is essential to expediting your development time and minimizing regression issues that reach QA or even production. By utilizing architecture patterns like MVP, MVC, MVI, or MVVM I aim to decouple as much business logic from my view as possible, thus allowing me to maximize my unit test coverage. Today we'll take a look at a few different ways you can improve your Android testing. We'll start with how to utilize Mockito to expedite unit test creation as well as reduce unit test execution speed that rely on Android APIs by utilizing Robolectric. Outside of unit tests it's important to also test the UI to assert current state after a sequence of simulated user gestures or input. This is called functional / instrumentation testing and is achieved by utilizing Espresso. Finally, we'll also cover how to maximize our code reuse for our Espresso tests by adhering to Jake Wharton's Robot Pattern.
As a continuation of the DebugRank series, this article follows up my previous blogs on Applying MVP in Android (where I setup my architecture for the DebugRank app) and Dependency Inversion & Dependency Injection (which demonstrates how to inject dependencies via the constructor so that you can later mock dependencies during unit testing).
Unit testing with Mockito
Mockito allows you to write unit tests that require dependency mocking without all the extra boiler plate code associated with manually creating mock dependencies. Mockito will only help you if you're already using some sort of dependency inversion to abstract the implementation details of your services.
Before we dive into using Mockito, it's important to consider the following scenario so that you can understand the problem it solves. In the code below, you can see in my LanguagesPresenterTests class that I'm manually creating mock dependencies of my LanguagesPresenter in order to minimize the code I'm testing to only the LanguagesPresenter. Additionally I want to test that my presenter retrieves the necessary data from the repository and passes it to the view, the easiest way to achieve this is to maintain a count of method calls in my dependencies.
Now you can see the same unit test using Mockito to mock my dependencies instead of manually creating them. Mockito allows me to easily assert the number of times a dependency method was called in a single line.
In some cases it might be necessary to override individual mocked methods in order to further test scenarios, an example being error handling. I can easily force a dependency to fail (throw an exception) in order to test my error handling. Below I'm testing to ensure that my view is correctly notified to show the error state when a dependency fails.
Mocking Android with Robolectric
Robolectric mocks the entire Android framework for you. This allows you to run tests that depend on the Android framework under the (test) folder instead of the (androidTest) folder, allow your unit tests to execute much faster. Remember that unit tests under (androidTest) must execute on an actual Android device, which ultimately will slow down unit test execution. Simply tell your unit test to run with Robolectric so that it knows to mock the Android framework for you. Do this by utilizing the @RunWith and @Config tag.
Functional testing with Espresso
Espresso allows you to perform functional/instrumentation testing, in order words allows you to simulate user input and verify UI state and navigation. Espresso tests must live in the (androidTest) folder as your app must actually be deployed and launched in order to simulate user input.
Login Screen example
Imagine I have a Login Screen that has an email field, password field, and login button. Some unit tests I might want to execute would include a successful login and unsuccessful login. In the Espresso tests below I'm simulating the user entering text for email and password and clicking the submit button. Then I can assert the state of the UI and what activity is shown to determine if the test passes.
The Robot Pattern is a design pattern for writing stable, readable, and maintainable tests. Looking at my two test methods I see some duplicate code, the code that queries for specific UI elements. Now imagine instead of only 2 tests I have 10 tests, the duplicate code is becoming slowly unmaintainable. Yes I could easily resolve this issue by writing a method, but what if my unit tests are split across multiple classes? What if this UI elements are actually part of a fragment that is shared by multiple activities, and I need to test the state of that fragment in each activity? This is where the Robot Pattern comes into play. The Robot Patterns easily allows me to encapsulate common Espresso queries/actions in a robot class. Using this pattern I can encapsulate login logic and assertion in the LoginRobot class and easily utilize my robot in my Espresso tests with a nice fluent API.
Build cross-platform native mobile apps Download the latest version of ComponentOne Studio Enterprise
Build cross-platform native mobile apps
Download the latest version of ComponentOne Studio EnterpriseDownload Now!