Unit Testing Bolts Tasks With Continuations (Android)

860 views Asked by At

I'm trying to write some tests for an Android application where we are integrating Bolts.

The object under test works by creating a Task and executing it in the background:

Task<MyResult> currentTask;

currentTask = MyTaskFactory.getImportantTask(parameters ...);

currentTask.continueWith(new Continuation<MyResult,MyResult>() {
        @Override
        public MyResult then(Task<MyResult> task) throws Exception {
            MyResult result = task.getResult();

            if (!task.isFaulted()) {
                if (DEBUG) {
                    logger.v("got login result back: " + result.toString());
                }

                if (result.getSuccess()) {
                    callback.onMySuccess(result.getData());
                } else {
                    callback.onMyError(result.getStatusCode(), result.getError());
                }
           }

            return result;
        }
    }, Task.UI_THREAD_EXECUTOR);

MyTaskFactory.getImportantTask() returns a Task, and my object under test gets this task and executes it in the background. When it completes, the completion should get executed.

I have verified that in the actual app, this code is working correctly. My problem arises because I am trying to run this under Robolectric/Mockito.

Here is the code under test:

myobject.runTaskWithContinuation(parameters...);

ShadowLog.d(TAG, "Waiting....");

Task<AuthenticationResult> task = controller.getCurrentTask();

assert(task != null);

ShadowApplication.runBackgroundTasks();
ShadowLooper.runUiThreadTasksIncludingDelayedTasks();

ShadowLog.d(TAG, "Finished!");

Using this code, and by mocking and instrumenting the calls made by the Task and the Continuation, I have verified that the Task is executing and generating a result, yet the Continuation never gets called. If I wait for the Task to complete, my unit test will never finish.

I am certain that I am doing something wrong here, but I really don't know what. How can I make sure that the Continuation gets called under test?

2

There are 2 answers

0
Craig Russell On

In your unit tests, since you have access to the Task directly, call

task.waitForCompletion()

For example:

@Test
public void directTaskAccess() throws InterruptedException {
    Task<Boolean> task = doWorkTask();
    task.waitForCompletion();
    assertTrue(task.getResult());
}

public Task<Boolean> doWorkTask() {
    return Task.callInBackground(() -> {

        // simulate doing 'hard' work
        Thread.sleep(1000);
        return true;
    });
}

For other tests when you don't have access to the Task reference, you might need to do a little extra work.

This blog post details more about it: Unit Testing Asynchronous Bolts Tasks

0
Nicholas Ng On

Use a CountDownLatch to make the Test thread to wait until your Continuation has been reached

here's a sample code that runs on my Robolectric test:

@Test
public void test1() throws Throwable {
    success = false;
    final CountDownLatch signal = new CountDownLatch(1);
    task.onSuccess(new Continuation<Object, Object>() {
        @Override
        public Object then(Task<Object> task) throws Exception {
            if (condition == true) {
                success = true;
            }
            signal.countDown();
            return null;
        }
    });
    signal.await(30, TimeUnit.SECONDS);
    assertTrue(success);
}