-
Notifications
You must be signed in to change notification settings - Fork 131
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
UI Tests: fix crash when running multiple tests #5445
Conversation
When running multiple tests, Hilt will give each test its own copy of components, but since Zendesk is using a static singleton, it will be using the same instance, so it will be already initialized for all subsequent tests. With this change, we will continue gracefully on this case.
if (isZendeskEnabled) { | ||
if (PackageUtils.isUITesting()) return | ||
else error("Zendesk shouldn't be initialized more than once!") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Honestly I don't know if this error here is useful for non-testing scenarios or not, but I decided to leave it as is.
You can test the changes on this Pull Request by downloading the APK here. |
This ensures that it's cleared when our scope is cancelled, which happens in UI tests
return EntryPoints.get( | ||
applicationContext, | ||
AndroidInjectorEntryPoint::class.java | ||
).injector() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Caching the AndroidInjector here meant having a different graph than the active one provided to its consumers when any subsequent test ran, this happened mainly in LoginWpcomService
which is part of the login library, and still uses AndroidInjector.
@@ -39,6 +40,7 @@ abstract class ApplicationModule { | |||
companion object { | |||
@Provides | |||
@AppCoroutineScope | |||
@Singleton |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to make sure we use the same instance of the coroutineScope across all components, for cancelling to work.
produceFile = { | ||
appContext.preferencesDataStoreFile("tracker") | ||
} | ||
}, | ||
scope = appCoroutineScope |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Passing the scope here fixes the issue of having multiple DataStores active when a second test is launched, since the first DataStore will be closed when the scope is canceled.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
❓ The default scope here CoroutineScope(Dispatchers.IO + SupervisorJob())
, the new one is CoroutineScope(SupervisorJob() + dispatcher)
where dispatcher
is a Dispatchers.Default
.
Are we sure we don't want DataStores
to operate on Dispatchers.IO
?
The IO dispatcher citing docs:
is designed for offloading blocking IO tasks to a shared pool of threads.
I'm afraid that DataStore
designers didn't expect it to run on CoroutineDispatcher
different than IO and that might lead to some unexpected behaviours. WDYT? Maybe we can change scope
only for UI tests?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice catch, we can derive a scope that uses Dispatchers.IO
from our app's CoroutineScope, done in cdaf2c7
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks a lot for spending your time on this @hichamboushaba !
I tried both command line execution and running a package from Android Studio, and it worked (I used Pixel 2 API 28, since it will likely be used on CI):
I will refrain from reviewing the code itself, since I don't have enough context and knowledge to do this effectively, but your explanations and changes made sense to me. 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the PR description, helped in understanding reasons here.
All tests passed, I'm leaving one concern.
produceFile = { | ||
appContext.preferencesDataStoreFile("tracker") | ||
} | ||
}, | ||
scope = appCoroutineScope |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
❓ The default scope here CoroutineScope(Dispatchers.IO + SupervisorJob())
, the new one is CoroutineScope(SupervisorJob() + dispatcher)
where dispatcher
is a Dispatchers.Default
.
Are we sure we don't want DataStores
to operate on Dispatchers.IO
?
The IO dispatcher citing docs:
is designed for offloading blocking IO tasks to a shared pool of threads.
I'm afraid that DataStore
designers didn't expect it to run on CoroutineDispatcher
different than IO and that might lead to some unexpected behaviours. WDYT? Maybe we can change scope
only for UI tests?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for addressing the comment, looks good!
Description
When running multiple tests during the same sessions, Hilt will reset the state after running the test, and will create new copies for all components for subsequent tests.
This PR makes the following changes to make UI tests work across multiple tests:
Zendesk.INSTANCE
, its state is being kept across tests, which causes a crash when we try to initialize the app, this PR relaxes the check we do onZendeskHelper
when running UI tests.Testing instructions
run
./gradlew connectedVanillaDebugAndroidTest -P android.testInstrumentationRunnerArguments.package=com.woocommerce.android.ui.main
and confirm tests run successfully.RELEASE-NOTES.txt
if necessary.