app-bundle-samples icon indicating copy to clipboard operation
app-bundle-samples copied to clipboard

Tests in dynamic feature module fail when run through gradle command line

Open RafaO opened this issue 5 years ago • 39 comments

I wrote some tests for a Dynamic Feature Module inside DynamicFeature/src/test/java/my/package/MyTest.kt. These tests rely on a class that is hosted in the main app module. If I run the tests using the Android Studio configuration they run without problems, but on the other hand if I use ./gradlew test they fail saying that the class on the main module can't be found:

    java.lang.NoClassDefFoundError
        Caused by: java.lang.ClassNotFoundException

In order to include the tests inside my CI, I would need either to know how to get the command line order that Android Studio is using, or figure out why when running the gradle task, it is not able to find the main module app.

Any help appreciated. Thanks in advance!

RafaO avatar Aug 01 '19 09:08 RafaO

I have the same problem and I also haven't found the solution.

linean avatar Aug 13 '19 11:08 linean

I'm facing the same issue as well. In my case code is in a common module shared by app and feature modules.

Full message is:

com.kouzoh.mercari.... > initializationError FAILED
    java.lang.NoClassDefFoundError
        Caused by: java.lang.ClassNotFoundException

Junit report:

java.lang.NoClassDefFoundError: Lcom/kouzoh/mercari/...;
	at java.lang.Class.getDeclaredFields0(Native Method)
	at java.lang.Class.privateGetDeclaredFields(Class.java:2583)
	at java.lang.Class.getDeclaredFields(Class.java:1916)
	at org.junit.runners.model.TestClass.getSortedDeclaredFields(TestClass.java:77)
	at org.junit.runners.model.TestClass.scanAnnotatedMembers(TestClass.java:70)
	at org.junit.runners.model.TestClass.<init>(TestClass.java:57)
	at org.junit.runners.ParentRunner.createTestClass(ParentRunner.java:88)
	at org.junit.runners.ParentRunner.<init>(ParentRunner.java:83)
	at org.junit.runners.BlockJUnit4ClassRunner.<init>(BlockJUnit4ClassRunner.java:65)
	at org.junit.internal.builders.JUnit4Builder.runnerForClass(JUnit4Builder.java:10)
...
Caused by: java.lang.ClassNotFoundException: com.kouzoh.mercari...
	at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:357)

pgreze avatar Aug 15 '19 01:08 pgreze

I have the same issue, no solution yet.

arohim avatar Aug 20 '19 07:08 arohim

Interesting,making a project that Google wont accept. I had not expected that anyway and i thougth that is possible to make something without going to Google.

svlima6669 avatar Aug 21 '19 12:08 svlima6669

Upgrading to com.android.tools.build:gradle:3.5.0 causes instrumentation tests in my dynamic feature modules to crash. Reverting back to 3.4.2 is the only solution I can find at the moment. Is this a known issue?

TomMarshBBC avatar Aug 21 '19 14:08 TomMarshBBC

Anyone has the solution for this issue?

wangchauyan avatar Nov 05 '19 23:11 wangchauyan

I have the same issue, no solution yet. Any solution? please?

luisansal avatar Nov 06 '19 15:11 luisansal

@luisansal I currently do such things:

  1. make sure the DFMs have the same Gradle setting as the App module.
  2. make sure the DFMs can access the classes of the App Module.

Then it works when I run the Gradle CLI

Another thought is: Maybe we can create interfaces in the App Module and implement these interfaces in DFMs, but that depends on how your project structures.

wangchauyan avatar Nov 06 '19 15:11 wangchauyan

Hi Mr Wang,

App Module classes, not found from other dynamic feature module (auth in my case), when run:

./gradlew auth:test:

I did the same gradle settings as the App module, but that doesn't work for me.

Team another solution?.

luisansal avatar Nov 06 '19 16:11 luisansal

@wangchauyan Can you please, share a part of your gradle code config, or ./gradlew command line?

luisansal avatar Nov 06 '19 18:11 luisansal

@luisansal
Hi,

Sorry, I can't share the code snap for you because it's internal codebase. Here are my understanding of your situation and possible solutions, I think your situation is that you have some classes in the App module. And your DFM (let's say auth) use these classes from the App module. You can write the test cases in auth module and import these classes from the App module.

But, when running the test case in auth module, you might sometimes encounter a few issues.

  1. Can't resolve the supertype of the class in the App module -> This means you might need to have the same Gradle implementation in your auth module cause the App module depends on some libraries.
  2. NoClassDefFoundError -> This means you might need to separate these shared classes out from the App module to a shared module for example, and you might need to create a jar file for your auth module or you can try something like this :
testImplementation project(path: ':App', configuration: 'tests')

in your auth module. Hope this helps.

wangchauyan avatar Nov 06 '19 18:11 wangchauyan

The code under test needs to be accessible from the dynamic feature module's (DFM) test classes. To enable this it either has to be in the DFM or included using androidTest(Implementation|Api). A way to keep it reusable throughout and not hard wire it to the DFM is to create a com.android.library module and depend on that.

keyboardsurfer avatar Dec 13 '19 10:12 keyboardsurfer

It would be nice to get a complete explanation of why this issue occurs. Android Studio can run the DFM tests without issue, and it seems to be manually passing the classpaths as CLI arguments. While refactoring code into a separate library module might fix it, in some cases this is a non-trivial task. We should not need to refactor significant amounts of our codebase just to be able to duplicate IDE functionality in a CI environment.

cc @jeffchang5

jeffypooo avatar Jan 15 '20 00:01 jeffypooo

@masterjefferson Can you please let me know which versions of Android Studio & build tools you're using for this?

keyboardsurfer avatar Jan 15 '20 09:01 keyboardsurfer

@keyboardsurfer I have tried both AS 3.5.3 and 3.6.0 RC 1 (with respective gradle plugin versions)

Build tools is 29.0.2, but also occurs with 28.0.3.

Gradle wrapper is using version 5.6.4.

jeffypooo avatar Jan 16 '20 17:01 jeffypooo

Same issue for me. Feature module tests fail with NoClassDefFoundError when running them in terminal on local machine or in CI. Tests pass in Android Studio.

I'm using AS 3.5.3, build tools 29.0.2, Gradle 5.6.4

aednlaxer avatar Jan 31 '20 09:01 aednlaxer

We're currently in the progress of testing a fix for this issue.

keyboardsurfer avatar Jan 31 '20 09:01 keyboardsurfer

This issue has been fixed and will be rolled out in one of the next Android Studio 4.0 canary releases.

keyboardsurfer avatar Feb 14 '20 21:02 keyboardsurfer

@keyboardsurfer is this hot-patchable to 3.6? we are having to disable tests in feature modules because of this.

chrisjenx avatar Feb 25 '20 15:02 chrisjenx

Hi @chrisjenx! Unfortunately this can't be easily patched as there were major changes. Thus the version code change from 3.x to 4.x.
Your best call is to upgrade to 4.x once you're ready for it or have two versions run in parallel.

keyboardsurfer avatar Mar 02 '20 11:03 keyboardsurfer

@keyboardsurfer I just updated my project to Android Studio 4 and 4.0.0-beta01 gradle plugin and am still seeing NoClassDefFoundErrors related to FragmentDirections - the generated NavDirections for my fragment which is located in a dynamic feature module

I only see this error when executing tests via the command line

MagsMagnoli avatar Mar 09 '20 21:03 MagsMagnoli

It should be okay from beta02. I am on 4.1 Canary 2 and things work well here.

keyboardsurfer avatar Mar 18 '20 20:03 keyboardsurfer

@MagsMagnoli I think it helps you

// This "test" source set is a fix for SafeArgs classes not being available when running Unit tests from cmd
// See: https://issuetracker.google.com/issues/139242292
sourceSets {
    getByName("test").java.srcDir("${project.rootDir}/app/build/generated/source/navigation-args/debug")
}

ulx avatar Apr 15 '20 14:04 ulx

There was another regression in the canaries. It has been resolved and should be shipped with Canary 6.

keyboardsurfer avatar Apr 16 '20 16:04 keyboardsurfer

i am currently dealing with this problem, nothing is working out

thapelo96 avatar May 03 '20 14:05 thapelo96

@thapelo96 Can you confirm that the issue is reproducible when running ./gradlew connectedAndroidTest --no-parallel using the sample code?

keyboardsurfer avatar May 04 '20 08:05 keyboardsurfer

I have the same issue on AS 4.0 even using this configuration: testRuntimeOnly(files("$projectDir$"/../app/build/intermediates/app_classes/debug/classes.jar"))

I have tried AS 4.1 Canary 10 and I have checked it works.

Is there any way to make it work on AS 4.0?

soygabimoreno avatar Jun 07 '20 07:06 soygabimoreno

I have the same issue on AS 4.0 even using this configuration: testRuntimeOnly(files("$projectDir$"/../app/build/intermediates/app_classes/debug/classes.jar"))

I have tried AS 4.1 Canary 10 and I have checked it works.

Is there any way to make it work on AS 4.0?

My fault! I have missed to add another dynamic feature with the testRuntimeOnly way. So, I have something like this: testRuntimeOnly(files("$projectDir$"/../app/build/intermediates/app_classes/debug/classes.jar")) testRuntimeOnly(files("$projectDir$"/../<dynamicFeatureName>/build/intermediates/app_classes/debug/classes.jar"))

soygabimoreno avatar Jun 09 '20 05:06 soygabimoreno

I am still seeing this error Caused by: java.lang.ClassNotFoundException: on AS 4.2 Canary 4

gaurigadkari avatar Jul 14 '20 22:07 gaurigadkari

I am still seeing this error Caused by: java.lang.ClassNotFoundException: on AS 4.2 Canary 4

It has to be resolved at Canary 6.

soygabimoreno avatar Jul 15 '20 03:07 soygabimoreno

I have the same issue on AS 4.0 even using this configuration: testRuntimeOnly(files("$projectDir$"/../app/build/intermediates/app_classes/debug/classes.jar")) I have tried AS 4.1 Canary 10 and I have checked it works. Is there any way to make it work on AS 4.0?

My fault! I have missed to add another dynamic feature with the testRuntimeOnly way. So, I have something like this: testRuntimeOnly(files("$projectDir$"/../app/build/intermediates/app_classes/debug/classes.jar")) testRuntimeOnly(files("$projectDir$"/../<dynamicFeatureName>/build/intermediates/app_classes/debug/classes.jar"))

For those still want to use 4.0 to supports product flavours

Modify the code into

testRuntimeOnly(files("$projectDir$"/../<baseApp>/build/intermediates/app_classes/<buildVariant>/classes.jar"))
testRuntimeOnly(files("$projectDir$"/../<dynamicFeatureName>/build/intermediates/app_classes/<buildVariant>/classes.jar")

Also make sure for /../ to have a correct path.

E.g with below directory structures:

|baseApp
|features
|--------/dynamicFeatureName

// needs to `cd` change directory twice to parent
testRuntimeOnly(files("$projectDir$"/../../<baseApp>/build/intermediates/app_classes/<buildVariant>/classes.jar"))
testRuntimeOnly(files("$projectDir$"/../../<dynamicFeatureName>/build/intermediates/app_classes/<buildVariant>/classes.jar")

mochadwi avatar Jul 21 '20 20:07 mochadwi

Not working for me even in Android Studio 4.2 Canary 7..

Also tried on AS 4.0.1 by adding the below line, but no luck testRuntimeOnly(files("$projectDir/../app/build/intermediates/app_classes/internalDebug/classes.jar")) testRuntimeOnly(files("$projectDir/../<dynamicFeatureName>/build/intermediates/app_classes/internalDebug/classes.jar"))

The error is same: Unresolved reference: BaseTest (Trying to refer BaseTest present in app inside the dfm's tests)

Please help here

AmritZoman avatar Aug 06 '20 18:08 AmritZoman

I am still seeing this error Caused by: java.lang.ClassNotFoundException: on AS 4.2 Canary 4

It has to be resolved at Canary 6.

I tried the Android Studio 4.2 Canary 7 as well, still doesn't work.

gaurigadkari avatar Aug 18 '20 21:08 gaurigadkari

Not working for me even in Android Studio 4.2 Canary 7..

Also tried on AS 4.0.1 by adding the below line, but no luck testRuntimeOnly(files("$projectDir/../app/build/intermediates/app_classes/internalDebug/classes.jar")) testRuntimeOnly(files("$projectDir/..//build/intermediates/app_classes/internalDebug/classes.jar"))

The error is same: Unresolved reference: BaseTest (Trying to refer BaseTest present in app inside the dfm's tests)

Please help here

You can't refer to test classes in another modules tests - that's not related to this issue. (I'm guessing your BaseTest is in app/src/tests and your accessing it via dfm/src/tests ?)

chrisjenx avatar Sep 01 '20 16:09 chrisjenx

@keyboardsurfer is there a new issue for this? As it shouldn't really be closed?

chrisjenx avatar Sep 01 '20 16:09 chrisjenx

FYI for all of those struggling with this workaround:

For library classes the path is different

testRuntimeOnly(files("${projectDir}/../../<library module path>/build/intermediates/compile_library_classes_jar/< buildVariant >/classes.jar"))

chrisjenx avatar Sep 01 '20 16:09 chrisjenx

I do not see this issue affecting the sample at hand.
Executing both ./gradlew test or ./gradlew connectedAndroidTest from within the DynamicFeatures project does not yield any failed tests.
In order to see what the actual issue is, I would have to see it either being reproducible in one of the samples in this repository or with a MCVE.

keyboardsurfer avatar Sep 15 '20 10:09 keyboardsurfer

Adding the line below to the dynamic feature module gradle file solved the issue on AS 4.1.1 (STABLE) Anyone knows whether the issue has been solved on any AS canary builds?

testRuntimeOnly(files("$projectDir/../app/build/intermediates/app_classes/<buildVariant>/classes.jar"))

JabezNzomo99 avatar Dec 10 '20 20:12 JabezNzomo99