{"id":19259420,"url":"https://github.com/tberchanov/countryapp","last_synced_at":"2026-05-19T14:08:30.356Z","repository":{"id":107225470,"uuid":"270915658","full_name":"tberchanov/CountryApp","owner":"tberchanov","description":"CountryApp - is a sample app representing usage of tests in multimodule Android project.","archived":false,"fork":false,"pushed_at":"2020-06-13T17:06:19.000Z","size":221,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-10-19T03:53:49.439Z","etag":null,"topics":["android","android-app","android-development","module-project","multimodule","testing","tests"],"latest_commit_sha":null,"homepage":null,"language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tberchanov.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-06-09T05:56:25.000Z","updated_at":"2023-10-23T10:22:12.000Z","dependencies_parsed_at":null,"dependency_job_id":"186cc3c4-82af-493d-be5c-9db0ddd8848e","html_url":"https://github.com/tberchanov/CountryApp","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/tberchanov/CountryApp","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tberchanov%2FCountryApp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tberchanov%2FCountryApp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tberchanov%2FCountryApp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tberchanov%2FCountryApp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tberchanov","download_url":"https://codeload.github.com/tberchanov/CountryApp/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tberchanov%2FCountryApp/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33219456,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-19T07:54:09.561Z","status":"ssl_error","status_checked_at":"2026-05-19T07:54:08.508Z","response_time":58,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["android","android-app","android-development","module-project","multimodule","testing","tests"],"created_at":"2024-11-09T19:16:33.577Z","updated_at":"2026-05-19T14:08:30.331Z","avatar_url":"https://github.com/tberchanov.png","language":"Kotlin","funding_links":[],"categories":[],"sub_categories":[],"readme":"CountryApp\n==========\n\nExample of using tests in Android multi-module project.\n\n## Intro\n\nNowadays multi-module Android architecture - is a standard and good practice.\nSeparation modules by features and layers brings to us better build time and well organized architecture.\nIn the same time unit and integration testing are also an essential part of development.\nI 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.\n\n## Problem: How to share utils test code across the modules?\n\nThe main pitfall here is that code from tests sources directories can not be used directly from another module.\nFor example in project you can find very useful `LogTestRule`, that placed in `tools_tests` module.\nLets try to use this rule in the test from `app` module.\n\n![alt text](https://github.com/tberchanov/CountryApp/blob/master/.readme_images/app%20-%20tools_tests%20dependency.png?raw=true)\n\nFor this we need to add `tools_tests` dependency in the `app/build.gradle`.\n```groovy\ndependencies {\n    // ...\n    testImplementation project(path: ':tools_tests')\n    // ...\n}\n```\n`testImplementation` is used because dependency on this module is required only for tests, and is redundant for release builds.\n\nThen lets use our rule in tests in the app module. IDE will allow us to do it without any warnings (Android Studio 4.0).\n\n![alt text](https://github.com/tberchanov/CountryApp/blob/master/.readme_images/test_without_warnings_example.png?raw=true)\n\nBut if we will try to run test from app module, the following compilation error will appear:\n\u003e Unresolved reference: LogTestRule\n\nAs `tools_tests` is a utils module that contains utils stuff only for modules, we can move utils code from test sources to main sources.\n\n![alt text](https://github.com/tberchanov/CountryApp/blob/master/.readme_images/tools_tests_directories_example.png?raw=true)\n\nAnd 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`.\n```groovy\ndependencies {\n    // ...\n    implementation 'junit:junit:4.12'\n    // ...\n}\n```\nSo now, everything should works. But as I said, such solution can be applied only for test utils modules.\nAnd here we come to the next problem.\n\n## Problem: How to share feature specific utils test code? \n\nIn the module `data_countries` we have tests for repository `RemoteCountriesRepositoryTest` and some utils functions for creating mocks in `CountriesCreator`.\nThis utils functions can be very useful for `domain_countries` module, as this module has test `CountriesMapperTest`, where entities from `data_countries` should be mocked.\n\n![alt text](https://github.com/tberchanov/CountryApp/blob/master/.readme_images/domain_countries%20-%20data_countries%20dependency.png?raw=true)\n\nThe previous solution can not be used here, because `data_countries` module contains not only testing code and `testImplementation` can not be used here.\nTheoretically we can move all testing code from `data_countries` to separate module, and name it `data_countries_tests`.\nIn this situation, previous solution can be used, but for me it looks like over-engineering.  \nFor such cases I would use another solution.\n\nLets create shared test sources inside `data_countries` module.  \nFor this you need to create folder `sharedTest/java/{module package name}` and move there code, that should be shared, `CountriesCreator` in our case.\n\n![alt text](https://github.com/tberchanov/CountryApp/blob/master/.readme_images/data_countries_directories_example.png?raw=true)\n\nThen you need to add the following code inside `android` block in `data_countries/build.gradle`:\n```groovy\nsourceSets {\n        test {\n            java.srcDirs += \"src/sharedTest/java\"\n        }\n        main {\n            if (Boolean.valueOf(project.findProperty(\"com.android.countriesapp.share.test.enabled\"))) {\n                java.srcDirs += \"src/sharedTest/java\"\n            }\n        }\n}\n```\n\nThere are a few interesting things:\n* `java.srcDirs += \"src/sharedTest/java\"` - just adds path to the list of source directories\n* `com.android.countriesapp.share.test.enabled` - is a property that enables or disables shared tests sources in the main sources.  \nIt 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.\nIn the same time we may need shared code in the main for debug builds or CI.  \nThis property is located in `gradle.properties` file.\n\nIf your shared code contains some testings library sdk, you should add this dependencies not only by `testImplementation`, but with `implementation` also.\nBut again, we don't need to have redundant dependencies for production ready builds, thats why we should enable them only by property.\nFor this reason, the following code should be written in `data_countries/build.gradle`:\n\n```groovy\ndependencies {\n    // ...\n    testImplementation \"org.mockito:mockito-core:3.3.3\"\n    if (Boolean.valueOf(project.findProperty(\"com.android.countriesapp.share.test.enabled\"))) {\n        implementation \"org.mockito:mockito-core:3.3.3\"\n    }\n    // ...\n}\n```\n\nAs result, the code from `sharedTest` source directory can be used across multiple modules.\n\nBut 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.  \nIf you want to get rid of these complexities, you can use the next solution.\n\n## Final solution\n\nFor example, we need to use `LoadCountriesUseCaseCreator` from `domain_countries` module in the `app` module.\n\n![alt text](https://github.com/tberchanov/CountryApp/blob/master/.readme_images/app%20-%20domain_counttries%20dependency.png?raw=true)\n\nFor this purpose we can write the following in the `app/build.gradle`:\n\n```groovy\nandroid {\n    // ...\n    sourceSets {\n        test.java.srcDirs += [\"${project(':domain_countries').projectDir}/src/test/java\"]\n    }\n}\n```\n\nAnd that's it, just almost one line solution for this problem, that seemed not trivial at the beginning.  \nI hope this project will help you. If you have other ideas how this problem can be solved, be sure to write me.  \nThanks for reading =)","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftberchanov%2Fcountryapp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftberchanov%2Fcountryapp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftberchanov%2Fcountryapp/lists"}