Android Testing (Kotlin) — How to test

Adalberto Plaza
6 min readOct 18, 2018

--

In this series of posts we are going to talk about Android Testing. It’s a big field to talk about, but here there are some important points and tips.

  1. Introduction.
  2. How to test.
  3. Unit tests.
  4. Integration tests.

Test structure

Now that we know how to create a test, let’s start with simple tests inside test directory. This is how a typical test looks:

  • @Before setUp(): this function is automatically run before every test. So, you can add here common initialization code you need: initialize variables, create objects and so on.
  • @After tearDown(): this is the opposite to the previous one. It’s automatically run after every test. So, you can add here every common code related to finish test cycle: reset states, remove items and so on.
  • @Test `Test title`(): this is the test itself. You can create as much tests as you need. Try to create one test per each atomic task. This way we will keep all tests as simple as possible. Try yo use a good descriptive title. Don’t be shy about long names, it’s a good way to quickly understand what the test does or what it proves. There are multiple test names conventions. Here is another possibility without the quotes. I usually use the following one in those cases.

should[do/return/throw]Whatever[When/On]SomethingHappens().

If I run the tests, this how the output looks when everything is OK. Green light!

However, if any test fails, a red state will be shown, pointing the failed test and the reason. In this case it expected to get 1, but it got 2. The stack-trace, if there were any crash, is attached as well. (Notice that here the error is simulated, so don’t pay attention to the actual reason)

Given-When-Then

Well, let’s talk about how every single test is organized inside it. In order to keep a generic and organized structure, and again according to Robert C. Martin, we can use a three block structure. This will help us, along with the descriptive title, to easily understand all tests from a project. With all tests having an homogeneous structure, is a matter of time to become familiar with the code and be able to quickly follow it, no matter if we have written them or not.

  • Given: we will set the test conditions here and create necessary objects. Do not confuse this with setUp() function. This block will affect only to the current test, while the setUp() method will affect to every single test.
  • When: this block is used to run the actions to test. This is basically, call the methods we want to test and get the results from them.
  • Then: finally, we will use this block to check that the results from the method execution are correct. In other words: the asserts. A useful tip is having only one assert per test or a focused group of them. This way we will keep tests simple, and will get direct test information about each atomic tests. If we mix functionalities while testing, we can misunderstand results, or some actions can even modify the behavior of other ones. Remember the I from FIRST principles: Independent.

Dependency injection in tests

This and the following one (Mocks and Test Doubles) are some of the very basic topics in testing. It turned out that a lot of coding strategies we do with our code base, are highly related with testing. It’s very important to make our code testable rather than just test the code. Let’s see an example of how important is dependency injection.

These methods are pretty simple. Let’s say we can use savePhoneNumber(number) to save a phone number into database with a DAO. If we think about testing isValidNumber(number)(we will see how in the next post), it’s pretty obvious we will just try different inputs on it and will check it behavior. But, what happens with savePhoneNumber(number), can we test it?. Well, the answer is: yes we can. But the right answer is: no, we shouldn't.

We should not test it, because it’s creating a new object inside it, which we can not control in the test. Think about the case when the test fails. Would we know the reason why or where it failed? We don’t know the logic inside PhoneNumberDao(), so we can not be sure if the failure it’s inside it or in our saver function. Remember this phrase: a key for successful testing is having a completely controlled test environment.

So, the correct way to write these methods in order to make them testable is the following one:

We are injecting the external class ( PhoneNumberDao) using the arguments in order to have the control over it. We could also inject it using the class constructor. If you have any doubt with dependency injection please refer to Dependency Inversion principle from SOLID.

You may ask, why I should do this? how is it helping me to control the external object? Let’s talk about the next point: mocks and test doubles.

Mocks and Test Doubles

Now that we have our code pretty much organized, let’s answer how to “control” the external dao. Remember that we want to test the number saver method isolated from the dao. We can, of course test the PhoneNumberDao in a separated test.

In order to get rid and control the external injection of the PhoneNumberDao, we are going to create a fake object. Or, in other words, create a mock or test double.

It’s really easy. We can create a new object extending from PhoneNumberDao(), mocking all its content. Or we can use an external tool like Mockito or Kluent (a layer over Mockito) to do it. They are testing frameworks to create fake or mocked objects and interact with them.

The definition for a basic mocked object, is a copy of the real object but without doing any operation; an empty object. In the case of we create a MockedDao it won’t save anything if we use create(..) method, and we won't know the returned result until we set it manually for the specific test. Basically we will have a dummy to play with.

// Kluent
val phoneNumberDao: PhoneNumberDao = mock()
When calling phoneNumberDao.create(anyString()) doReturn true
// In new version doReturn has changed to itReturns

Having the control over the object we can set it’s behavior. When calling phoneNumberDao.create(whatever) a true is returned. No matter what is expected to happen inside. Pretty descriptive syntax. Note: we can make it more sophisticated checking the passed object with eq(..) instead of anyString()

As we mentioned before, we can create the mock manually as well instead of using Kluent, and use it for the test. This is specially useful when we need to add extra logic inside the mocked object.

And here is the final code of how this simple test looks. It follows the three blocks: it prepares the mocked object and the number, then it call the saver method, and finally it checks the result.

Remember that we are testing savePhoneNumber() method, so it doesn’t matter what the dao is doing, we just want to be sure the saver method does what it’s supposed to do. Notice that we can test the validation method as well, but we will see the complete example in the next post.

Originally published at https://adalpari.github.io on October 18, 2018.

--

--