Friday 31 May 2019

How to allow a busy working thread in Espresso

I'm trying to test the progress indicator of an app, which shows an indeterminate progress indicator while the view model is fetching data.

In order to test that, I'm mocking the provider that returns the data and make it block until my test gives tells it to go ahead. The basic setup looks like this:

@Test
public void testProgressIndicator() {
    injectProvider(mockProvider);
    startTestActivity();

    // The mock provider now runs on a worker thread and won't finish
    // until we tell it to.

    // We should now see a progress indicator.
    onView(withId(R.id.progress_indicator)).check(
        matches(withEffectiveVisibility(Visibility.VISIBLE)));

    // Tell the worker thread to finish up.
    mockProvider.setResult();

    // The worker thread now returns a result, the progress indicator
    // should be gone.
    onView(withId(R.id.progress_indicator)).check(
        matches(withEffectiveVisibility(Visibility.GONE)));
}

This code is old, so the provider is using blocking code on a worker thread using AsyncTask.

However, Espresso normally waits for all workers to finish in order to make sure the results are not dependent on the timing. In particular, it uses the AsyncTaskPoolMonitor to wait for all pending AsyncTask objects. This is normally great, but in my case, I want this thread to remain busy while Espresso continues. How can I tell Espresso not to wait for this particular thread?

The cheap cop-out would be to just a Thread and communicate via Handler or something similar, but it would be nice to find a solution while retaining the setup using AsyncTask.

When I break into the debugger, I see that my test runner thread is stuck on the first check():

wait:-1, Object (java.lang)
parkFor$:2137, Thread (java.lang)
park:358, Unsafe (sun.misc)
park:190, LockSupport (java.util.concurrent.locks)
await:2059, AbstractQueuedSynchronizer$ConditionObject (java.util.concurrent.locks)
take:442, LinkedBlockingQueue (java.util.concurrent)
gatherAnyResult:83, InteractionResultsHandler (androidx.test.espresso)
gatherAnyResult:52, InteractionResultsHandler (androidx.test.espresso)
waitForAndHandleInteractionResults:314, ViewInteraction (androidx.test.espresso)
check:300, ViewInteraction (androidx.test.espresso)
testProgressIndicator:76, MyFragmentTest (com.my.test)  <<< ***********************
invoke:-1, Method (java.lang.reflect)
runReflectiveCall:50, FrameworkMethod$1 (org.junit.runners.model)
run:12, ReflectiveCallable (org.junit.internal.runners.model)
invokeExplosively:47, FrameworkMethod (org.junit.runners.model)
evaluate:17, InvokeMethod (org.junit.internal.runners.statements)
evaluate:80, RunBefores (androidx.test.internal.runner.junit4.statement)
evaluate:531, ActivityTestRule$ActivityStatement (androidx.test.rule)
evaluate:20, RunRules (org.junit.rules)
runLeaf:325, ParentRunner (org.junit.runners)
runChild:78, BlockJUnit4ClassRunner (org.junit.runners)
runChild:57, BlockJUnit4ClassRunner (org.junit.runners)
run:290, ParentRunner$3 (org.junit.runners)
schedule:71, ParentRunner$1 (org.junit.runners)
runChildren:288, ParentRunner (org.junit.runners)
access$000:58, ParentRunner (org.junit.runners)
evaluate:268, ParentRunner$2 (org.junit.runners)
run:363, ParentRunner (org.junit.runners)
run:104, AndroidJUnit4 (androidx.test.ext.junit.runners)
runChild:128, Suite (org.junit.runners)
runChild:27, Suite (org.junit.runners)
run:290, ParentRunner$3 (org.junit.runners)
schedule:71, ParentRunner$1 (org.junit.runners)
runChildren:288, ParentRunner (org.junit.runners)
access$000:58, ParentRunner (org.junit.runners)
evaluate:268, ParentRunner$2 (org.junit.runners)
run:363, ParentRunner (org.junit.runners)
run:137, JUnitCore (org.junit.runner)
run:115, JUnitCore (org.junit.runner)
execute:56, TestExecutor (androidx.test.internal.runner)
onStart:388, AndroidJUnitRunner (androidx.test.runner)
run:2075, Instrumentation$InstrumentationThread (android.app)



from How to allow a busy working thread in Espresso

No comments:

Post a Comment