Testing in Android (MVP) – Part 1

In this first post of a three-part series, we will walk through unit testing an android application that uses the MVP architectural pattern. By the end of the three-part series, we will have discussed unit testing, integration testing, and instrumentation testing. The first two will allow us to run tests very quickly on our local development machine. For the third, we will need to set up an emulator or use a physical device to run the tests. This series will both show you the benefits of having chosen the MVP pattern as well as the various libraries and frameworks that help us write tests against this architecture.

Jumping Right In

Picking back up with the MVPDemo from the previous post, where we built a Tip Calculator app, we are now going to test our code.

Test our code? But that’s a waste of time, we have to ship!

With automating our unit tests we can provide:

  1. scale
  2. insurance
  3. peace of mind

In other words, with automated unit tests, we can ensure the code that we wrote last week does the same thing this week, even if someone else has committed a new feature.

The two-thirds Rule

In Android, we should test at least 2 out of the 3 things from the list below to ensure that our app’s core functionality works:

  • Unit tests – the smallest possible test we can write which asserts an actual value matches an expected value.
  • Integration tests – tests that ensure the proper integration of 2 or more components. (think presenter in the MVP pattern)
  • Instrumentation tests – tests that run on a physical or emulated device. These are not fast tests but they can be useful in a CI system or if you want to make sure things display as expected (asserting views are on screen or contain certain text, clicking this button performs this action, etc.).

Okay so now that we know the types of tests we should be writing let’s ask the real question…

Is there really a two-thirds rule?

Well, no. The point is you should consider covering the majority of your code with some tests. Being able to confidently know that two-thirds of the behavior of the code you have just written is guaranteed will drastically improve your process as an engineer and team member.

In my previous post, discussing the MVP architectural pattern, we can see how the decoupling of our code allows us to more easily test the functionality within our app. Specifically, we can see how the three test types we just mentioned apply to the MVP pattern.

As you can see, we can test the model with fast unit tests using either JUnit , AssertJ or Robolectric. We can test our presenter using a combination of AssertJ & MockK (Mockito if you prefer it too MockK). And finally, we can test our view code using the Espresso library or UIAutomator . These are just a subset of the popular testing frameworks available to us now but let’s get to the unit testing for this first part.

Setup

Before we get started we have some setup to take care of to import the correct dependencies for testing. In the top-level build.gradle file, add the following line (e.g denoted by the // 1):

and in your app level build.gradle file add the lines (e.g denoted by // 1, // 2 // 3):

  1. Adds the plugin for JUnit 5 to work with Android
  2. Adds the test/kotlin directory to our test source sets
  3. Adds the JUnit 5, AssertJ and MockK testing dependencies

Writing a Unit Test

One of the most important areas to test within our code base is our app’s model code. No, I am not talking about testing a POJO or POKO (Kotlin variant), I am talking about the Model used to manage the data within our application! The code that determines the actual behavior of the functionality provided for a view. We have to test this code to guarantee our app works as expected. But what does this look like?

MainModel code (for reference):

Looking at our model code we see it provides the following functionality for use:

  • MainModel() (default constructor)
  • setTipOption(position: Int) : Unit (sets the tip option and returns nothing)
  • computeTotalPrice(price: String?): Double (computes the price returns total)

Since we don’t have any constructor arguments lets start by assuring the setTipOption code works as expected. We want to assert that with a valid index, the tipOption is set to the expected value.

Okay, so what did we just do? Well, we set the tip option to 0 (the first index of the array in our model) which sets our tip to 5% so when we compute the total price of $100 check, we should expect that we get back a value of $105.0 for 5% of $100 = $5.

We have written our first passing test but if we run the same test with code coverage we will notice that we haven’t tested the full source code for this method. So, let’s take a look at one other test case: the invalid tip option. If you look back up at the MainModel code, we can see that if the index of the argument is out of range of valid tip options we default to a 20% tip. We will want to make sure that code works too!

Okay perfect all code within the setTipOption method is now covered and the behavior works as expected. Going forward, if any of the code in this method changes we now have a couple of tests to make sure we don’t break the expected behavior. If we do break the expected behavior (assuming a new feature or new default is chosen by the product team…), we will update the test case and move on. Additionally, we want to test out the functionality of the computeTotalPrice method.

Since this method is a bit more complicated we have to test it with at least 3 tests:

  1. Test that a good price will yield the expected result
  2. Test null or empty price yields 0.00 return value
  3. Test if we have a negative price that all values will be 0.00

Well done! 100% test coverage for a single class and the best part is in under 1 second we can now test that the MainModel code works as expected. As an exercise, write some tests to exhaust the tip calculator options testing the remaining percentages return the expected values.

All of the source code can be found on GitHub.

Summary

Great job! Now you know how to write unit tests that guarantee the behavior of your tip calculator’s model code. In this post, you have learned that there are 3 types of tests you should consider when it comes to Android application development. You have learned about some of the frameworks used in the testing process. And you have written your own unit tests.  In my next post, I will show you how to test the presenter code in the MVP pattern to guarantee the integration between the View and Model.

If you enjoyed this post please share or comment! And feel free connect with me on social media.