I am creating a user interface test for an Android app. The test taps a UI element and expects another UI element to appear. The test will only pass if I include a Thread.sleep after tapping the first UI element. How can I avoid this?
Here's the test:
#Test
public void mapInfoIsDisplayed() throws InterruptedException {
// Tap on the crosshair at the centre of the map.
onView(withId(R.id.map_crosshairs)).perform(click());
Thread.sleep(100); // Why is this required?
// Verify that the view is displayed
onView(withId(R.id.feature_title)).check(matches(isCompletelyDisplayed()));
}
The app is written in RxJava, uses Hilt for dependency injection and Espresso as the UI testing framework.
I want the test dependencies to run synchronously on the main thread so my test uses:
the trampoline scheduler for RxJava during tests (executes on the calling thread)
an in-memory Room database which allows queries on the main thread
I have also disabled animations through the emulator settings and have also added the following to my build.gradle (although I'm not sure it does anything):
testOptions {
animationsDisabled = true
}
With all this I would expect the test to run synchronously, waiting for any dependent threads to finish before testing for whether my the new view is displayed. This doesn't appear to be happening so I'm left with a flaky test.
I have a strong suspicion that the production code is being executed asynchronously somewhere, the problem is I can't pinpoint where (it'd be great if there was some way in Android Studio to find this).
How can I ensure all code is executed synchronously, thus avoiding the Thread.sleep?
Related
I'm using the Android Test Orchestrator to run my UI tests and i need to generate a report of the results when all the tests are finished.
I need to have a callback for when the orchestrator starts running the tests and when it finishes running all of them in a way that i can generate and save my report.
Given that a new instance of the AndroidJUnitRunner is created for every tests, i can't take advantage of onStart() and finish() to do that.
I was looking for a way to maybe provide a custom orchestrator so i can use the internal methods..
I am having an activity which has camera, and having a mask over the camera which is done by onDraw() method, I was writing test cases for the navigation drawer which is above the camera screen, but getting error as
Perhaps the main thread has not gone idle within a reasonable amount of time? There could be an animation or something constantly repainting the screen. Or the activity is doing network calls on creation? See the threaddump logs. For your reference the last time the event queue was idle before your activity launch request was 1472804618974 and now the last time the queue went idle was: 1472804618974. If these numbers are the same your activity might be hogging the event queue
I have already turned off the animation as already mentioned over
https://google.github.io/android-testing-support-library/docs/espresso/setup/index.html
Don't know what needs to be done.
I have already turned off the animation as already mentioned over
https://google.github.io/android-testing-support-library/docs/espresso/setup/index.html
This problem occurs not because of turned of animations, but because of Espresso framework character: Espresso depends on your actual application context.
Espresso for Android is perfect and fast test automation framework,
but it has one important limitation - you are allowed to operate only
inside your app under test context.
That means that it is not possible to automate tests for such app
features like:
application push notifications
contact synchronization
navigating from another app to your app under test,
since you have to deal with other apps from the mobile device -
Notification Bar, Contacts or People app, etc.
From: http://qathread.blogspot.com/2015/05/espresso-uiautomator-perfect-tandem.html
It means that Espresso tests are based on UI thread and waiting when the main thread is idle().
Perhaps the main thread has not gone idle within a reasonable amount of time? There could be an animation or something constantly repainting the screen. Or the activity is doing network calls on creation? See the threaddump logs. For your reference the last time the event queue was idle before your activity launch request was 1472804618974 and now the last time the queue went idle was: 1472804618974. If these numbers are the same your activity might be hogging the event queue.
This means that your main thread is actually busy and Espresso can't perform any new actions, it's waiting for free idling resources to run.
To solve this problem you have these options:
write your own custom Espresso IdlingResource to say Espresso when he can perform action, in other words: IdlingResource tells Espresso when the main thread is idle and ready to take a new actions.
exclude appcompat and support-v4 from espresso libs : https://github.com/googlesamples/android-testing/issues/56
I suppose that you're running Camera using an Android Intent, so it isn't included in your actual app context, it's not displayed on your apps activity/fragment. If it is true, try to use UiAutomator along with Espresso framework. Read: http://qathread.blogspot.com/2015/05/espresso-uiautomator-perfect-tandem.html
instead of Espresso try to use Robotium, Appium or another instrumentation framework without this limitation.
Already, I'm using along Espresso, Robotium and UiAutomator, so don't be afraid to work with mix of them.
Hope it help
So i was able to solve my issue.
The problem was occurring due to continuous calling method invalidate() from onDraw().
Instead i replaced it with postInvalidateDelayed(), and for certain conditions(like animations) only used invalidate().
It helped! :)
Avoid continuous repainting screen.
I'm using IdlingResource to synchronize a few network related tasks. I register and unregister idlingresource in the #Before and #After methods. The instrumentation is AndroidJUnitRunner.
The basic sequence of steps are:
Espresso clicks a button which makes a network request
When I get response, espresso clicks another button which starts a new activity.
Make more network requests via the new activity.
What actually happens though, is that the first network request is made but new activity is not started. I have a feeling that the main thread is looping somewhere, but I can't pinpoint it.
When I execute a single test (right click method, click on "Run testmethod..") it works, but when I try to execute all the test methods in the class, it fails.
The cool thing is even if every method is empty, save for one which actually does the UI testing, it still fails.
A similar question is in this thread, but has no answers: Espresso 2 on Android, intermediately tests fail after failing to start the activity under test while activities from previous tests are still alive
Any help will be appreciated.
I'm running a test with Robolectric runner. The code under test verifies it's not executed on the main thread:
if (Looper.getMainLooper().getThread() == java.lang.Thread.currentThread()) {
new IllegalStateException("Method called on the UI thread");
}
The Robolectric test raises this exception, and I don't want that. I tried running the code from a Robolectric.getBackgroundScheduler(), but I'm still getting the exception.
How can my test run in a different thread?
The main idea in testing multithreading code is to make it run in controlled way on a single thread.
What I would do:
Move checking code to some class helper
Inject it and mock it under the test
Pluses of this solution:
It will resolve your issue
It will remove duplication and move you closer to SRP (single class responsibility principle)
Minuses:
It requires proper naming since it will hide functionality behind method
It will give you additional flexibility that you might not need
Success!
How do you create unit tests for an Android activity that starts async tasks in onCreate? I would like to test the result of these tasks.
It is hard to write tests for a lot of Android functionality, since you can't instantiate classes like Activity outside of Android.
You might be better off doing a true unit test...test the function whose behavior you care about in isolation. Don't try to test it in the context of async task, activity, etc.
You might need to refactor your code a little bit to be able to do that, but its worth it to have testable code!
Running true units tests as mentioned in Cheryl's answer would be ideal. However if you still find yourself wanting to test the result AsyncTasks or any long running asynchronous operation in an Activity Test, Espresso is the silver bullet.
Espresso automatically waits for AyscTasks to complete and the developer can manually tell Espresso to wait for custom background tasks running via the IdlingResource APIs.
Here's a tutorial to help you get started: http://blog.sqisland.com/2015/04/espresso-custom-idling-resource.html
IdlingResource documentation: http://developer.android.com/reference/android/support/test/espresso/IdlingResource.html