Question

I want to use Espresso to test web calls. I've made a small test app to see if I can get this to work without using AsyncTask. Upon getting data back, a TextView tells the user whether the call was successful or not. I'm making the web call in a new thread.

In my test, I tried following the AdvancedSynchronizationTest as a pattern to making the test wait until the web request is fulfilled. My implementation, however, doesn't seem to work. My understanding is that the method that I'm overriding is ending before the callback is returned to the main activity. If that's the case, I'm overwriting the wrong class. I've also tried overriding runnable itself (see example below) and even changed the UI update to simply setting a boolean, but with no different outcome. Obviously, Espresso doesn't like my using another thread, but why doesn't it pick up that it should wait until the thread is complete? What would be an alternative without using AsyncTask?

You can see the whole project here: https://github.com/bqmackay/EspressoCustomThreadingResourceExample.

Here is the code in question:

MainActivityTest

public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActivity> {
...
    @Override
    protected void setUp() throws Exception {
        super.setUp();
        getActivity();

        Runnable runnable = getActivity().getDownloadHelper().getWebCallRunnable();
        CountingIdlingResource countingIdlingResource = new CountingIdlingResource("WebCallRunnableCall");
        getActivity().getDownloadHelper().setWebCallRunnable(new DecoratedWebCallRunnable(runnable, countingIdlingResource));
        registerIdlingResources(countingIdlingResource);
    }

    public void testThreadRetrieval() throws Exception {
        onView(withText("Thread")).perform(click());
        assertTrue(getActivity().isDidThreadReturn());
        //onView(withText("Thread Retrieved")).check(matches(isDisplayed()));
    }

    private class DecoratedWebCallRunnable implements Runnable {

        private final Runnable realRunnable;
        private final CountingIdlingResource countingIdlingResource;

        private DecoratedWebCallRunnable(Runnable realRunnable, CountingIdlingResource countingIdlingResource) {
            this.realRunnable = realRunnable;
            this.countingIdlingResource = countingIdlingResource;
        }

        @Override
        public void run() {
            countingIdlingResource.increment();
            try {
                realRunnable.run();
            } finally {
                countingIdlingResource.decrement();
            }
        }
    }

DownloadHelper

public class DownloadHelper {
    private Runnable webCallRunnable;

    private Runnable createWebCallRunnable() {
        return new Runnable() {
        @Override
        public void run() {
            HttpClient httpclient = new DefaultHttpClient();
            HttpResponse response = null;
            try {

                response = httpclient.execute(new HttpGet("http://whoismyrepresentative.com/getall_mems.php?zip=84020"));
                StatusLine statusLine = response.getStatusLine();
                if(statusLine.getStatusCode() == HttpStatus.SC_OK){
                    ByteArrayOutputStream out = new ByteArrayOutputStream();
                    response.getEntity().writeTo(out);
                    out.close();
                    repsCallbackInterface.onRepsThreadReceived(true);
                } else{
                    //Closes the connection.
                    response.getEntity().getContent().close();
                    repsCallbackInterface.onRepsThreadReceived(false);
                    throw new IOException(statusLine.getReasonPhrase());
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        };
    }
}

MainActivity

public class MainActivity extends Activity implements RepsCallbackInterface {
    ....
    @Override
    public void onRepsThreadReceived(final boolean didReceive) {
        setDidThreadReturn(true);
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                threadText.setText(didReceive ? "Thread Retrieved" : "Thread Failed");
            }
        });
    }
}
Was it helpful?

Solution

You didn't really provide a question here, so I assume the question is about why this setup is not working as expected.

There are (at least) two reasons:

  1. The assert assertTrue(getActivity().isDidThreadReturn());in testThreadRetrieval() does not wait at all for IdlingResources to be finished. Only ViewInteraction.check() and ViewInteraction.perform() are aware of those and will check IdlingResources before trying to apply the ViewAssertion or ViewAction.

  2. Even if the commented out line after the assertTrue() is applied instead, it would not work, because the instrumentation thread is independent and doesn't wait for the new thread created in getReps() to start, so the check of IdlingResources can and will happen before the CountingIdlingResource is incremented.

If, for whatever reason, you really don't want to use an AsyncTask, you still can profit from Espresso watching the AsynTask thread pool by using its executor. So instead of starting a separate thread, just submit the Runnable to the AsyncTask.THREAD_POOL_EXECUTOR.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top