Android App Testing. Practical steps
In this post, I’ll show you how we covered our code with tests. But first I’d like to give a brief overview of our app architecture. It is a pretty popular combination of Clean Architecture and MVP with Kotlin and RxJava inside.
- Presentation layer implemented with MVP
includes Android components (Activity, Fragment) as a View and Presenter, responsible for running use cases; also presenter maps business objects (BO) into view models - Domain layer based on RxJava
use cases are returning basic reactive streams (Completable, Single, Observable); under the hood, they use repositories from the data layer and map data transfer objects(DTO) into BO. - Data layer based on RxJava
repositories include all methods to manipulate data; they use different kinds of data sources inside (local storage, remote API, system services, etc)
Given … When … Then …
Every test consists of three sections:
- Given (conditions)
- When (action is taken)
- Then (make assertions)
It’s a good practice to separate these sections with a blank line to improve the readability of the test.
You can also use this pattern in the naming of test methods: testGivenNetworkErrorWhenRefreshRequestedThenErrorShouldBeDisplayed
(this probably requires team agreement).
Writing tests when you don’t have enough time
When you just created a new feature your test coverage is zero. You need to choose a good starting point to create the most effective tests first.
It’s always better to start with unit tests, as they cover core business logic and can be run on nearly every commit.
Considering you have a typical feature, you will have different layers to be covered with tests.
Here is a suggested order of test writing, from high to low importance:
- Use cases and other business logic.
- Presenters and their interactions with views, use cases.
- Repositories and data source combining logic.
- Mappers for DTO-BO and BO-ViewModel transitions.
- Simple UI test checking screen opening.
- Integration tests working with stub data or real API.
- Simple UI test checking basic features of the screen (not checking specific text size, colors, positions, etc).
- End to End tests covering user scenarios.
Testing tools
Here are the libraries currently used in our project:
- JUnit 4 is a mainstream unit testing framework
- Mockito is a mainstream mocking tool
- Mockito-Kotlin https://github.com/nhaarman/mockito-kotlin/wiki
is a Mockito wrapper to simplify the library usage for Kotlin code - Kluent https://github.com/MarkusAmshove/Kluent
Assertion library created specifically for Kotlin - Barista https://github.com/SchibstedSpain/Barista
A set of utilities and assertions intended to avoid boilerplate code for some common actions/checks in UI tests
Tip #1
JUnit 4 creates new instance of test class to run each test, so if you need to do some initialization “before class”, make sure you use static members:
class SampleTest {
companion object {
@JvmStatic
lateinit var repository: DataRepository
@BeforeClass
@JvmStatic
fun setup() {
repository = mock(...)
}
}
}
Tip #2
Initializing mocks is an expensive operation. When possible, reset mocks to speed up tests.
class SampleTest {
companion object {
@JvmStatic
lateinit var repository: DataRepository
}
@After
fun reset() {
Mockito.reset(repository)
}
}
You can find tests classification in my related article: