Test a class that involves listener callback inside AsyncTask

Viewed 1296

I'm trying to test a listener callback that is being triggered inside an AsyncTask,

Listener class :

interface LoaderListener {
    fun onByteSuccess(..., ..., ...)

    fun onByteFailure(..., ...)
}

The class containing AsyncTask :

class Loader {

override fun handleStreamTask(){
        InputStreamHandlingTask(..., ...).execute(byteArray)
    }

private inner class InputStreamHandlingTask constructor(
            internal var ...,
            internal var ...
        ) : AsyncTask<ByteArray, Void, ByteArray>() {

            override fun doInBackground(vararg params: ByteArray): ByteArray? {
                val response = params[0]
                .....
                return response
            }

            override fun onPostExecute(byteArray: ByteArray?) {
                   if (byteArray != null) {
                       listener.onByteSuccess(..., ..., ...)
                   } else {
                       listener.onByteFailure(..., ...)
                   }
            }

        }
}

The test I'm trying to make :

@Test
fun testIfListenerCalled(){
    val loader: Loader = mock()
    val loaderListener: LoaderListener = mock()

    loader.handleStreamTask()

    verify(loaderListener).onByteSuccess(..., ..., ...)
}

The error I'm curently encountering:

Exception in thread ... java.lang.RuntimeException: Method execute in android.os.AsyncTask not mocked. See http://g.co/androidstudio/not-mocked for details. at android.os.AsyncTask.execute(AsyncTask.java)

2 Answers

If this is a unit test that is running on your local machine instead of running on an Android device, you cannot mock classes that depend on the Android framework such as the AsyncTask. Instead one should implement this as an instrumented test instead of a unit test which runs on the Android device or use framework which can mock the Android framework on your local machine.

More info here: https://developer.android.com/training/testing/unit-testing/instrumented-unit-tests

Below example demonstrates how to test Asynctasks in JUnit.

/**
 * @throws Throwable
 */
public void testAsynTask () throws Throwable {
    // create  a signal to let us know when our task is done.
    final CountDownLatch signal = new CountDownLatch(1);

    /* Just create an in line implementation of an asynctask. Note this
     * would normally not be done, and is just here for completeness.
     * You would just use the task you want to unit test in your project.
     */
    final AsyncTask<String, Void, String> myTask = new AsyncTask<String, Void, String>() {

        @Override
        protected String doInBackground(String... arg0) {
            //Your code to run in background thread.
            return "Expected value from background thread.";
        }

        @Override
        protected void onPostExecute(String result) {
            super.onPostExecute(result);

            /* This is the key, normally you would use some type of listener
             * to notify your activity that the async call was finished.
             *
             * In your test method you would subscribe to that and signal
             * from there instead.
             */
            signal.countDown();
        }
    };

    // Execute the async task on the UI thread! THIS IS KEY!
    runTestOnUiThread(new Runnable() {

        @Override
        public void run() {
            myTask.execute("Do something");
        }
    });

    /* The testing thread will wait here until the UI thread releases it
     * above with the countDown() or 30 seconds passes and it times out.
     */
    signal.await(30, TimeUnit.SECONDS);

    // The task is done, and now you can assert some things!
    assertTrue("Expected Value", true);
}
Related