Closed zpiao1 closed 2 years ago
Hi, @zpiao1 I guess this is a good idea to create a PR with above changes
Yes I was considering creating a PR. But just now I did some more testing and realized that if the test app does not have any compose dependency the espresso server will also crash (which is common if the test app only has views but not compose).
I guess we might need a better way to remove the duplicated class (androidx.compose.ui.R.string
) only instead of removing the whole dependency of androidx.compose.ui:ui
Most probably synchronizing the versions between your app under test and the espresso server will make it work. you can customize which version to use in espresso using espressoBuildConfig
capability
I've tried to use the same version of androidx.compose.ui:ui
for both app under test and the espresso server.
The default version in espresso server is 1.0.5 so I changed my app's androidx.compose.ui:ui
to 1.0.5.
I've also tried to use espressoBuildConfig
like this in the appium client:
caps['appium:espressoBuildConfig'] = json.dumps({
'toolsVersions': {
'gradle': 7.4,
'androidGradlePlugin': '7.1.1',
'compileSdk': 31,
'buildTools': '31.0.0',
'targetSdk': 31,
'kotlin': '1.6.10',
},
'additionalAndroidTestDependencies': [
'androidx.compose.ui:ui-test:1.1.0',
'androidx.compose.ui:ui-test-junit4:1.1.0',
]
})
with the app under test using Compose 1.1.0. Both result in the same Exception when there is a TextField Composable
can you please set capability forceEspressoRebuild: true to make sure a new server was built? Also, does changing compose version to 1.1.0 directly in the espresso code fix your issue?
I've enabled forceEspressoRebuild together with other options
caps['appium:forceEspressoRebuild'] = True
caps['appium:showGradleLog'] = True
caps['appium:enforceAppInstall'] = True
and from appium logs I do see the Gradle is running every time I start the tests
I also tried to go to the appium-espresso-driver directory in appium/node_modules
to change the compose version in the Version
object. However, the result is still the same with the same Exception. After failing, I go and check the temp folder that builds the espresso server and verified that the compose version there is updated.
I also tried to inspect the APKs of both the espresso server (in the temp folder that is to be installed on device), and the AUT. And I found that the same field of the same class (androidx.compose.ui.R$string.default_error_message
) actually has different value
This is the screenshot of the espresso server APK:
This is the screenshot of the AUT APK:
When I had similar issue, I had to see Gradle dependencies graph to match exact version of dependencies. For eg, you can try with below espressoBuildConfig
capability
espressoBuildConfig: '{"additionalAndroidTestDependencies": ' +
'["androidx.lifecycle:lifecycle-extensions:2.2.0", ' +
'"androidx.activity:activity:1.3.1", ' +
'"androidx.fragment:fragment:1.3.4"]}'
Oh I tried adding your espressoBuildConfig
capability and it worked!
In fact, it's the fragment dependency that made it work.
Here is my full espressoBuildConfig
:
caps['appium:espressoBuildConfig'] = json.dumps({
'toolsVersions': {
'gradle': 7.4,
'androidGradlePlugin': '7.1.2',
'compileSdk': 31,
'buildTools': '31.0.0',
'targetSdk': 31,
'kotlin': '1.6.10',
},
'additionalAndroidTestDependencies': [
'androidx.compose.ui:ui-test:1.1.1',
'androidx.compose.ui:ui-test-junit4:1.1.1',
'androidx.fragment:fragment:1.3.4', # I also tried 1.4.1 and it also worked
]
})
I have no idea of how it worked and I tried to print the gradle dependencies.
Here is output after running ./gradlew app:androidDependencies
in the temporary folder that is used to build the Espresso server, after applying all the changes above (SDK, Kotlin version, etc.)
And here is the output of ./gradlew app:assembleDebug
running in the project that builds the AUT
There is actually no dependency on androidx.fragment:fragment
in the AUT, so I guess it's what androidx.fragment:fragment
depends that took effect.
The problem is if there is a way to determine what are the necessary additionalAndroidTestDependencies
instead of guessing one by one?
androidx.fragment:fragment
could be a transitive dependency of one of your app modules. It's tough to say without looking at actual code. But for most cases, below should work in similar case (versions of dependencies matching with the app under test)
try with below
espressoBuildConfig
capabilityespressoBuildConfig: '{"additionalAndroidTestDependencies": ' + '["androidx.lifecycle:lifecycle-extensions:2.2.0", ' + '"androidx.activity:activity:1.3.1", ' + '"androidx.fragment:fragment:1.3.4"]}'
Hi, I find another issue with Compose support from espresso driver. When the Composable contains a
TextField
, espresso server fails to start, with theResource$NotFoundException
. However, opening the app from launcher doesn't crash.Reproducing
Just a minimal example Composable that contains a TextField will cause the error.
This issue happens both when using Compose 1.0.X or 1.1.0.
Full Stacktrace
Possible Reason
It took me a long time to find out what's causing the issue. From the stacktrace, the
TextFieldImpl
is trying to get a resource string as the default error message (Source), which in turn gets the resource stringandroidx.compose.ui.R.string.default_error_message
(Source).However, the value of this resource is different depending on where we call it. (I have no idea why)
I've tested the below cases:
androidx.compose.ui.R.string.default_error_message
in the androidTest code of the espresso driver, the result is0x7f080003
androidx.compose.ui.R.string.default_error_message
in the test app, the result is7f0a0004
(Can be other values, but different from the value in 1)androidx.compose.ui.R.string.default_error_message
is reference from Compose Material itself, the ID is also0x7f080003
, same as in 1.Workaround
I also checked the other issue #449 and think whether it's the same issue. The fixes in that issue kind of inspired me that it might be because both the espresso server APK and the test app's APK contain the same class
androidx.compose.ui.R.string
therefore in runtime there is some conflicts.After experimenting, changing the dependencies in app module's build.gradle.kts like this can fix the issue.
This excludes
androidx.compose.ui:ui
from the APK that contains the espresso server. Thus, so long as the test app hasimplementation("androidx.compose.ui:ui:${Version.compose}")
, the classes used eventually are from the test app instead of from the espresso server.After rebuilding the espresso server, the test can start successfully.