Android App Testing. Test types
There are several types of tests you can create for Android app:
- Unit tests
The fastest ones, intended to tests one method or one class. Unit tests are grey box tests. We should test the interactions between the object and its dependencies but not the internal structure of the object. - Instrumented tests
Medium tests running on device or Robolectric. Useful to test isolated screens and integration between modules/layers. This kind of tests ideally should be black-box tests, but they can include only several specific layers of the app. - End to End tests
High-level tests to check user journeys. Can be used in TDD or as acceptance criteria. This kind of tests is always black-box. It covers all layers of the app with possible stubs for I/O operations.
Generally, unit tests are much easier to implement and support. Also, they are way faster, so we can run them on CI frequently.
The suggested proportion between these three types of tests is 70–20–10.
Unit tests
Unit tests are usually related to a specific class or even function. The main goal here is to fully test the logic of this particular piece of code, without testing other related components.
Here are some tips to check if you on the right way:
- Try to isolate business logic from Android SDK classes as much as possible. This way it can be tested with unit tests.
- Break logic into pieces and test them isolated. For example,
Story
mapper should better have a dependency on theStoryItem
mapper. This way you can test mapping of single item isolated and exclude it fromStory
mapper test. - Create a new use case to combine data from several repositories or from existing use cases.
- Test negative cases when other components fail to provide any data.
- All test inputs must be explicitly specified in a test. It must not depend on device locale, current date or time, any randomized input.
Testing use cases
Generally, use cases should only use repositories and mappers and combine the data.
So the objective here is to check interactions between components and make sure there are no unexpected interactions.
Testing presenters
The presenter is a middleware between view and use cases. It can also contain additional logic.
The goal is to make sure presenter reacts to view events properly and at the same time delivers data from repositories.
It is also important to check error handling, especially if screen has some expected errors to be shown in UI.
Testing repositories
The repository usually contains the logic of choosing and combining data sources (REST API, local storage, system services).
So, the main point here is to test this logic of interaction with other components.
Error handling is also important part here.
Testing mappers
Mappers should generally have just one method, so all the tests should check that with given input we get the expected output.
Basic tests should cover mapping fully filled object and empty one. There can be other corner cases that should be tested specifically.
Consider extracting sub-mapper if some part of an entity has complicated mapping logic. For example, Story
mapper should better have a dependency on theStoryItem
mapper. This way you can test mapping of single item isolated and exclude it from Story
mapper test.
Integration tests
The idea of integration testing is to check how several components of your app are working together. A test can cover all layers from REST API to Android UI or it can have limited scope from Repository to Presenter.
End to End tests
End to End test should work in a production environment and check complete user scenarios. It is always a black-box test, it knows nothing about application components. All it can do is to mimic real user, so scenarios will generally be like “Fill in the username, fill in the password, click the button Login and check that new screen is open”.
These tests are easy to break because they depend on screen layouts and navigation. So, it is not recommended to write end to end tests for every scenario and every screen. The good starting point is to find the most important cases which are not supposed to change frequently.
For practical steps on writing tests you can check out the following article: