24

I have an Android Studio project with two library modules: foo-module and bar-module. Each implements a library, with foo-module defining a strategy interface and bar-module depending upon foo-module and implementing such a strategy. foo-module has instrumentation tests (foo-module/src/androidTest/) to test its core code, using a stub strategy implementation, and bar-module should have its own instrumentation tests.

I defined an AbstractTests class in foo-module/src/androidTest/ that does most of the actual testing. I also have a StubTests class in foo-module/src/androidTest/ that extends AbstractTests and implements the necessary abstract methods to complete the test case (providing an implementation of the strategy, etc.). This all works great.

In bar-module/src/androidTest/, I created a BarStrategyTests class, designed to mirror StubTests, but provide the strategy implemented in bar-module. However, BarStrategyTests cannot see AbstractTests, even though I have compile project(':foo-module') in my build.gradle file, and the main (non-test) classes in bar-module can work fine with the main (non-test) classes in foo-module. IOW, while the project() dependency handles regular code, it does not handle androidTest/ code. I get "error: package com.commonsware.foo.test does not exist".

I tried also adding androidTestCompile project(':foo-module'), with the same result.

What is the recipe for sharing instrumentation test code between modules?

Temporarily, I can clone AbstractTests, but that's not a great long-term solution.

This SO question covers similar ground for ordinary Java. Has anyone tried the options in the one answer and gotten them to work for Android instrumentation tests? The first option (move the common testing code into yet another module as regular non-test code) seems plausible, but I have no idea if the other two will work well with the com.android.library plugin instead of the java plugin.

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • did you find a way to achieve to this? I want to make my test classes available for the other modules too, but having difficulties to share the resources. – X-HuMan Jan 15 '17 at 14:39
  • 1
    @karate: I am using the approach outlined in the accepted answer. – CommonsWare Jan 15 '17 at 14:40
  • There should be a better way to do this instead of moving the all the test classes to their own module and extending from them :( Someone who is responsible for gradle should make it easier for us to extend test classes – Sheraz Ahmad Khilji Aug 28 '18 at 10:21
  • Solution for Android + Kotlin projects: https://stackoverflow.com/a/63298267/12001093 – 05nelsonm Sep 27 '20 at 09:21

1 Answers1

22

Due to the fact that all the test classes (unit and instrumentation) are not a part of any module, including aar, they are not available via dependency to that module. I faced to this issue as well and solved it by creating test-module and put all the required classes in it (src/main/java). So, in your case you can move AbstractTests in this module and use this module as a androidTestCompile dependency.

Oleg Khalidov
  • 5,108
  • 1
  • 28
  • 29
  • 1
    I can definitely see needing to do something different if I were depending upon a pre-packaged AAR, such as a repository artifact. I had expected that `compile project(':foo-module')` would match up the appropriate sourcesets -- after all, it's not `compile projectButJustTheMainSourcesetPlease(':foo-module')`. :-) I'm going to leave this open for a while to see if anyone else has another option, but I will not be shocked if yours winds up being the best option. Thanks for the help! – CommonsWare May 06 '16 at 18:53
  • 1
    Note that this approach seems to require multiple additional modules. Basically, `foo-module` cannot have any tests of its own. Not only does `AbstractTests` have to get moved to `foo-module-test-common`, but any tests formerly in `foo-module` have to get moved to `foo-module-test`. Otherwise, you get circular dependencies: `foo-module` has `androidTestCompile` on `foo-module-test-common`, which has `compile` on `foo-module`. `foo-module` cannot have a dependency on `foo-module-test-common` as a result. What a pain. – CommonsWare May 07 '16 at 22:24
  • 3
    Well, you have to avoid `foo-module` depends on `foo-module-test-common`. One option is to move all the tests from `foo-module` to `foo-module-test-common` so this module not just contains common test-classes in `src/main/java` but also contains the tests for `foo-module` in `src/androidTest/java`. – Oleg Khalidov May 07 '16 at 22:34
  • Ah, good point. I wasn't thinking of that combination. Thanks again! – CommonsWare May 07 '16 at 23:03
  • @CommonsWare were you able to come up with a proper solution for this? I'm facing a similar issue, but my "base" test utils that I want to be inherited cross-module rely on stuff from `org.junit.*` (e.g. test rules) which don't work in `main/java`... – Droidman Mar 10 '20 at 08:54
  • @Droidman: That project was a few years ago, but IIRC wound up doing more or less what is here. AFAIK, a library can use `org.junit.*` classes in its `main` code -- you just need to have the JUnit dependency as a regular one (e.g., `api` or `implementation`) and not a test one (e.g., `androidTestApi` or `androidTestImplementation`). – CommonsWare Mar 10 '20 at 11:21
  • @CommonsWare oh, thanks :) Indeed I have just copy-pasted the test dependencies from another module so it was `testImplementation`. Adding jUnit, AssertJ et al. as "real" dependencies works just fine. – Droidman Mar 11 '20 at 14:27