https://github.com/tberchanov/countryapp
CountryApp - is a sample app representing usage of tests in multimodule Android project.
https://github.com/tberchanov/countryapp
android android-app android-development module-project multimodule testing tests
Last synced: 29 days ago
JSON representation
CountryApp - is a sample app representing usage of tests in multimodule Android project.
- Host: GitHub
- URL: https://github.com/tberchanov/countryapp
- Owner: tberchanov
- License: mit
- Created: 2020-06-09T05:56:25.000Z (about 6 years ago)
- Default Branch: master
- Last Pushed: 2020-06-13T17:06:19.000Z (about 6 years ago)
- Last Synced: 2025-10-19T03:53:49.439Z (8 months ago)
- Topics: android, android-app, android-development, module-project, multimodule, testing, tests
- Language: Kotlin
- Size: 216 KB
- Stars: 2
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
- Code of conduct: CODE_OF_CONDUCT.md
Awesome Lists containing this project
README
CountryApp
==========
Example of using tests in Android multi-module project.
## Intro
Nowadays multi-module Android architecture - is a standard and good practice.
Separation modules by features and layers brings to us better build time and well organized architecture.
In the same time unit and integration testing are also an essential part of development.
I faced with a few not very obvious things in combining this two common approaches in project and want to share my knowledge and experience with community.
## Problem: How to share utils test code across the modules?
The main pitfall here is that code from tests sources directories can not be used directly from another module.
For example in project you can find very useful `LogTestRule`, that placed in `tools_tests` module.
Lets try to use this rule in the test from `app` module.

For this we need to add `tools_tests` dependency in the `app/build.gradle`.
```groovy
dependencies {
// ...
testImplementation project(path: ':tools_tests')
// ...
}
```
`testImplementation` is used because dependency on this module is required only for tests, and is redundant for release builds.
Then lets use our rule in tests in the app module. IDE will allow us to do it without any warnings (Android Studio 4.0).

But if we will try to run test from app module, the following compilation error will appear:
> Unresolved reference: LogTestRule
As `tools_tests` is a utils module that contains utils stuff only for modules, we can move utils code from test sources to main sources.

And as now testing code placed not in test sources, we need to change dependency way of JUnit from `testImplementation` to `implementation` in `tools_tests/build.gradle`.
```groovy
dependencies {
// ...
implementation 'junit:junit:4.12'
// ...
}
```
So now, everything should works. But as I said, such solution can be applied only for test utils modules.
And here we come to the next problem.
## Problem: How to share feature specific utils test code?
In the module `data_countries` we have tests for repository `RemoteCountriesRepositoryTest` and some utils functions for creating mocks in `CountriesCreator`.
This utils functions can be very useful for `domain_countries` module, as this module has test `CountriesMapperTest`, where entities from `data_countries` should be mocked.

The previous solution can not be used here, because `data_countries` module contains not only testing code and `testImplementation` can not be used here.
Theoretically we can move all testing code from `data_countries` to separate module, and name it `data_countries_tests`.
In this situation, previous solution can be used, but for me it looks like over-engineering.
For such cases I would use another solution.
Lets create shared test sources inside `data_countries` module.
For this you need to create folder `sharedTest/java/{module package name}` and move there code, that should be shared, `CountriesCreator` in our case.

Then you need to add the following code inside `android` block in `data_countries/build.gradle`:
```groovy
sourceSets {
test {
java.srcDirs += "src/sharedTest/java"
}
main {
if (Boolean.valueOf(project.findProperty("com.android.countriesapp.share.test.enabled"))) {
java.srcDirs += "src/sharedTest/java"
}
}
}
```
There are a few interesting things:
* `java.srcDirs += "src/sharedTest/java"` - just adds path to the list of source directories
* `com.android.countriesapp.share.test.enabled` - is a property that enables or disables shared tests sources in the main sources.
It is not good to add shared tests sources to the main for release builds, as we don't want to have such code in the final apk.
In the same time we may need shared code in the main for debug builds or CI.
This property is located in `gradle.properties` file.
If your shared code contains some testings library sdk, you should add this dependencies not only by `testImplementation`, but with `implementation` also.
But again, we don't need to have redundant dependencies for production ready builds, thats why we should enable them only by property.
For this reason, the following code should be written in `data_countries/build.gradle`:
```groovy
dependencies {
// ...
testImplementation "org.mockito:mockito-core:3.3.3"
if (Boolean.valueOf(project.findProperty("com.android.countriesapp.share.test.enabled"))) {
implementation "org.mockito:mockito-core:3.3.3"
}
// ...
}
```
As result, the code from `sharedTest` source directory can be used across multiple modules.
But this solution looks too complicated. We have a lot of overhead stuff like property, shared folder and duplication of dependencies for code from shared folder.
If you want to get rid of these complexities, you can use the next solution.
## Final solution
For example, we need to use `LoadCountriesUseCaseCreator` from `domain_countries` module in the `app` module.

For this purpose we can write the following in the `app/build.gradle`:
```groovy
android {
// ...
sourceSets {
test.java.srcDirs += ["${project(':domain_countries').projectDir}/src/test/java"]
}
}
```
And that's it, just almost one line solution for this problem, that seemed not trivial at the beginning.
I hope this project will help you. If you have other ideas how this problem can be solved, be sure to write me.
Thanks for reading =)