4

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)

questionasker
  • 2,536
  • 12
  • 55
  • 119
Falling Into Infinity
  • 697
  • 3
  • 13
  • 34
  • Since nobody else came around to answer this, maybe its time to look at a different framework ... see https://stackoverflow.com/questions/33556172/why-doesnt-mocking-work-with-asynctask – second Jul 05 '19 at 19:24

2 Answers2

1

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

ahasbini
  • 6,761
  • 2
  • 29
  • 45
  • This is correct. To get the test to run on your device, the first step is to move the tests from the test directory to the androidTest directory. – drdaanger Jul 12 '19 at 15:42
0

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);
}
Sachin Kasaraddi
  • 597
  • 5
  • 12