Understanding Android Testing
“Does the product work as expected?”
“Can I integrate future changes seamlessly, with minimum side effects on existing features?”
These are two of the primary reasons why testing is considered a key area in software development. Testing can sometimes be a very complex task, especially on large codebases with several dependancies including network, databases and on Android, it’s framework. In this post I am going to give a walkthrough of testing Android applications from what I understand. If there’s anything I’ve missed or can improve on, please let us know in the comments below.
In Android, there are two types of test cases we can run: Local JVM tests and Instrumentation tests. Local JVM tests are those which we can run directly on our machines and requires zero Android framework dependancies. I try to keep these test cases concise as possible and limit them to a single thread of execution, and try to cover the smallest block of logical code per test case. Instrumentation tests on the other hand are test cases which require Android framework components like Context, Activity, etc. and must be run on a real device or emulator. These tests can cover complex use cases with multiple threads of execution, so that they mimic the real world application as much as possible.
But this branching is still too coarse to try to understand and cover an app’s use cases effectively. I tried to get a better undestanding of these two types of testing and came across this test pyramid.
This pyramid breaks down an application into the different areas which can be tested seperately. Then, by categorising these areas into either local or instrumentation tests, I was able to get a much lower level understanding of testing applications effectively. The diagram below should give you the same.
Note how Integration tests fall under both categories. I will explain why shortly.
Unit Tests: Focus mainly on application logic. Try to keep these as concise as possible with very fast execution. JUnit can be used to verify test results and Mockito for mocking out objects and methods. You can read more about these tools here http://junit.org/junit4/ and http://site.mockito.org.
Integration Tests: Integration tests focus mainly on your app’s dependancies. You can test your database, network calls, etc integration here and verify if they are functioning as expected. The reason why I put Integration tests under both categories is because of this nice tool called Roboelectric. Roboelectric mimics the Android framework and your app’s framework dependancies on your local machine, so you don’t need a device or emulator. However, when testing network calls, databases, etc. on your local machine (JVM tests) I suggest mocking out the actual services to return test data quickly and keeping it as light as possible on a single thread. The real world use cases with the actual services and background threads can be tested as Integration test cases under Instrumentation tests with real devices or emulators.
UI Tests: The only part remaining for us to test now is the user interface. These tests make sure the UI behaves as it is expected. For example, we could verify if a TextView is set to visible when a Button is clicked. Tools like Espresso, Robotium and UI Automator can be used to write these test cases.
I hope this post was helpful in giving an understanding of testing android applications. If you’ve got any suggestions or improvements please share them with us in the comments below.