Android testing is nearly always a forgotten or discarded task. It’s criticized as being boring, difficult or a waste of time. But it’s not! It’s a really important part of the development process and yes!, it could be fun and simple.
A little of my personal experience
At my university, previous job, here at Wolox and in every other place where I learned how to code, I was encouraged thousands of times to run tests and I think other software developers would say the same thing. In theory, we all know we must run tests but when it comes down to it nobody does it or it’s done with a very low quality.
Since in my current project the rule is to test every class I add, I searched how to do it and I found out the benefits of this. It was really simple and it could be even fun.
Why should we test?
The most important and simpler answer to this question is to be sure that a part of the code is working as expected. But also, it’s important to be sure that when we are adding new features, we are not breaking any previous ones. By doing this, our project will be reliable and maintainable over time.
The main guiding principles we should take care of when we test are:
- Fast: if we are doing it right, the project will have a lot of tests so it’s really important that each test takes very little time to run.
- Isolated: the test should only address the part of code we want so it must be independent of any other element. For example, it could be a response of a service or a file or anything else.
- Reliable: it could be obvious but the test should fail only when the code has a bug.
- Understandable: we should be able to know where and why our project is failing when a test fails only by its name so we must write it as expressive as possible.
So we will add a class inside the “test” package named “«TestedClassName»Test”. The folders tree will be like this:
Here is an example of a simple one:
As we can see, the tests will be methods preceded with the annotation
@Test. It’s important to remember that giving it an understandable name is one of the principles that we need to take care of. There are many conventions to write them so we need to define one for the entire project. Also, with Kotlin we are allowed to add spaces to make them more understandable. To verify if a behaviour is right, we used
assertThat(result, expectedResult) that compared a method’s return value with its expected result.
However, what happens if the behavior we are verifying uses a variable element like an API? Remember that one principal that we need to address is isolation. So, to accomplish this we will use Mockito. A Mock is an object that simulates the behavior of another one. With this we will configure something we don’t control, like an API, to perform the behavior we want. Also, it allows us to check if a method is called or not. For example, in an MVP architecture, it will be useful for the view when testing the presenter. An example of how to use Mockito:
In this example, we defined the mocks with the
@Mock annotation and we verified that a method is called with
verify(object, times).method(). To instantiate the mock, we executed the
MockitoAnnotations.initMocks(class) and we did it inside a method preceded with the
@Before annotation. All the methods preceded by that will be executed before each test and the same will happen with the
@After annotation. But if we want to execute it once before or after all test we should use the
To add one instrumented test, we will need to follow the same steps as for unit testing but in the “androidTest” package. An important difference between unit and instrumented testing is that we can’t use spaces in the names here. Another difference is that when we run a behavior we need to click on a Button, or write in an EditText, or any interaction with the real device. Here we can use Espresso which gives us the tools to do that and here is an example of how to do it.
@RunWith(AndroidJunit4::class) annotation precedes the class meaning that the class will run Android instrumented tests. The
@Rule annotation will precede a class variable that will be instantiated with the ActivityTestRule. This is responsible for instantiating the activity.
To run the actions on the activity, we found the view we want with
onView(withId(R.id.id_name)) and performed the action with
view.perform(action). Then we checked the expected UI result with
Note: When we run the Android instrumented test we need to make sure that the screen device where we will run it is unlocked.
After checking the importance of testing, at Wolox we are working on improving the quality of our code and our projects. To achieve this we are writing documentation, creating many examples and a base as a starting point.
We hope all developers will start running tests and encouraging his/her partners inside and outside of the work environment to do it too.