Falling in love with Android Testing
As pointed out in my previous posts (Android architecture and Dependency Injection), one main purpose of using a proper architecture is to test our codes easily.
For software development, generally, our works which are covered by tests will be really awesome, and you are definitely confident about your codes. It is even better that we follow test-driven development(TDD) when developing our beloved products.
Because of its importance, this article is going to cover the general picture of software testing, and Android, specifically.
Which kinds of test do we have in general?
Software testing has its long history, and you can find a lot of resources about it on the internet. Basically, there are several types of test as following.
Tests the smallest unit of functionality, typically a method/function (e.g. given a class of a particular state, calling x method on the class should cause y to happen). Unit tests should be focussed on one particular feature (e.g., calling the pop method when the stack is empty should throw an
Integration tests build on unit tests by combining the units of code and testing that the resulting combination functions correctly.
Functional tests check a particular feature for correctness by comparing the results for a given input against the specification. Functional tests don’t concern themselves with intermediate results or side-effects, just the result (they don’t care that after doing x, object y has state z).
Acceptance testing seems to be split into two types:
Standard acceptance testing involves performing tests on the full system (e.g. using your web page via a web browser) to see whether the application's functionality satisfies the specification. E.g. "clicking a zoom icon should enlarge the document view by 25%." There is no real continuum of results, just a pass or fail outcome.
Also, in agile software development, user acceptance testing involves creating tests to mirror the user stories created by/for the software’s customer during development. If the tests pass, it means the software should meet the customer’s requirements and the stories can be considered complete. An acceptance test suite is basically an executable specification written in a domain specific language that describes the tests in the language used by the users of the system.
The above information is a part of Mark Simpson’s answer on StackOverflow.
When coming to Android, there are two common view points for us to divide and name test types.
Android test types based on running environments
Firstly, we can distinguish the tests running on our local Java Virtual Machine(JVM) and the tests running on our Android platform(in which we need devices or emulator).
Local Unit Test
Many parts of our Android apps are pure Java codes which only need JVM to run on, so we can write unit tests for these parts.
- Test code location:
module-name/src/test/java/. Make sure to put all of your local unit tests here. Gradle will know to run them on JVM only.
Local unit tests can even be used to test our codes which depend on Android framework. In that case, mock objects are our friends.
Because we are developing Android apps, many of our parts need to access Android framework API such as UI, SharedPreferences, and so on. If we write tests for these parts without mocking, they should run on Android devices or emulators.
- Test code location:
module-name/src/androidTest/java/. Make sure to put all of your instrumented test here. Gradle will know to run them on your connected devices or emulators.
A picture is worth a thousand words. Here it is.
Now you already understand why Android Studio automatically generates three different places for you under src folder when creating a new app.
module-name/src/main/java/— Your main Java source code folder.
module-name/src/test/java/— Your local unit test folder.
module-name/src/androidTest/java/— Your instrumented test folder.
Android real test types
The above way of dividing tests is specific for Android apps and is easy for us to imagine. However, Android testing is a subset of general software testing, it is highly recommended to understand them in term of the real testing types. It means that Android test types should fall into general types in the previous section. Commonly, we consider two main types only (Unit test and Integration test).
- Unit Tests (60–70 % of test code base)
- Local Unit Tests: Unit tests that run locally on the Java Virtual Machine (JVM). Your tests do not depend on or mock the Android framework.
- Instrumented Unit Tests: Unit tests that run on an Android device or emulator. These tests have access to Android API and mock objects do not satisfy.
- Integration Tests
- Components within your app only: This type of test verifies that the target app behaves as expected when a user performs a specific action or enters a specific input in its activities.
- Cross-app Components: This type of test verifies the correct behavior of interactions between different user apps or between user apps and system apps.
In summary, we should keep in mind that local unit tests are relatively fast, easy-to-debug compared to instrumented tests. That is the reason why the local unit test is preferred and we should mock Android framework dependencies when we need instead of using devices or emulators.
Besides, there are many libraries and frameworks which support Java and Android testing out there such as JUnit, Mockito, Roboelectric, Espresso, UIAutomator, and you name it. To keep this article to be concise, I would like to cover their usage in another article.
Your app is cool and it would be awesome if you write testable codes which are covered by tests.