Skip to content
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

Merged
merged 9 commits into from
Dec 14, 2021
Merged

Conversation

hichamboushaba
Copy link
Member

@hichamboushaba hichamboushaba commented Dec 13, 2021

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:

  • since Zendesk is using a static singleton 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 on ZendeskHelper when running UI tests.
  • We use DataStore in the app, but without providing our own CoroutineScope, it will stay active when the test is finished, this PR makes sure we pass our CoroutineScope, and we cancel it properly after finishing the test.
  • The login library is not migrated to Hilt yet, and instead it still uses AndroidInjector, in our Application for tests, we were caching the AndroidInjector in a field, but this meant that after a second test is launched, we'll keep track of the past graph, which meant using nonconsistent components (different instances of Stores), and failure during login.
  • The FCMMessageService is called sometimes on app launch if a token is received, which leads to a race condition where the injection of the service happens before Hilt tests are initialized (check this for more details), since we don't need this service for tests, I added a dummy implementation that does nothing.

Testing instructions

run ./gradlew connectedVanillaDebugAndroidTest -P android.testInstrumentationRunnerArguments.package=com.woocommerce.android.ui.main and confirm tests run successfully.

  • I have considered if this change warrants user-facing release notes and have added them to RELEASE-NOTES.txt if necessary.

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.
@hichamboushaba hichamboushaba added the category: ui tests Related to UI testing. label Dec 13, 2021
@hichamboushaba hichamboushaba requested review from pachlava, a team and kidinov and removed request for a team December 13, 2021 14:26
@hichamboushaba hichamboushaba marked this pull request as ready for review December 13, 2021 14:26
Comment on lines +101 to +103
if (isZendeskEnabled) {
if (PackageUtils.isUITesting()) return
else error("Zendesk shouldn't be initialized more than once!")
Copy link
Member Author

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.

@peril-woocommerce
Copy link

peril-woocommerce bot commented Dec 13, 2021

You can test the changes on this Pull Request by downloading the APK here.

@hichamboushaba hichamboushaba removed the request for review from kidinov December 13, 2021 14:45
@hichamboushaba hichamboushaba marked this pull request as draft December 13, 2021 14:45
Comment on lines +31 to +34
return EntryPoints.get(
applicationContext,
AndroidInjectorEntryPoint::class.java
).injector()
Copy link
Member Author

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
Copy link
Member Author

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
Copy link
Member Author

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.

Copy link
Contributor

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?

Copy link
Member Author

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

@hichamboushaba hichamboushaba marked this pull request as ready for review December 13, 2021 18:19
@hichamboushaba hichamboushaba modified the milestones: 8.2, 8.3 Dec 13, 2021
Copy link
Contributor

@pachlava pachlava left a 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):

Screenshot 2021-12-13 at 22 32 44

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. 👍

Copy link
Contributor

@wzieba wzieba left a 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
Copy link
Contributor

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?

Copy link
Contributor

@wzieba wzieba left a 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!

@wzieba wzieba merged commit a955722 into develop Dec 14, 2021
@wzieba wzieba deleted the ui-tests-hilt branch December 14, 2021 12:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
category: ui tests Related to UI testing.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants