Testable Android AsyncTask

Please also read this post for an explanation regarding asynchronous operations and unit testing performance after reading this post.

Android AsyncTasks are cool. No doubt about that. However, while reading and refactoring code from existing projects I have seen a lot of code that uses AsyncTask in a wrong way. This code is not testable and also does not respect the Law Of Demeter. In this post, I will describe how to avoid this anti-pattern and present the pattern I currently use for implementing testable AsyncTasks.

AsyncTask anti-pattern

To see how not to design your AsyncTasks, just go to the AsyncTask documentation on the Android Developer Site. The code at the time of this writing looks like this:

Now imagine you have to write a unit test for this code. Besides the inherent problem of testing asynchronous things, which I’ll cover later in this post, you might have noticed that the only way to test for success would be to actually check for the presence of that dialog that is displayed in the onPostExecute method.

I hope we all agree that this is evil :) It is not clean code, it breaks the Law Of Demeter, the SRP, and other principles, and, ultimately, is hard to test.

Testing asynchronous things

Before we get to the actual refactoring of AsyncTasks for easy testability, I’ll spend a few words talking about testing asynchronous things in general.
After trying several things, I have resorted to use CountDownLatch. From the Android Developer site:

A synchronization aid that allows one or more threads to wait until a set of operations being performed in other threads completes.

Sounds like it could be what we are looking for. Let’s keep this in the back of our heads for a while, we’ll get back to it soon.

AsyncTask clean-code style

Refactoring the AsyncTask from the Android Developer site to be clean is easy and involves three steps:

  1. Create an interface for the actual action to take place when the AsyncTask finishes
  2. Create a wrapper around the AsyncTask that uses the dependency injection pattern with the interface created in step 1.
  3. Make the AsyncTask use the interface methods

Create an interface for the actual action to take place when the AsyncTask finishes

For simplicity’s sake, let’s ignore the progress for now and stick to the result of the download. With that in mind, our interface could look something like this:

Create a wrapper around the AsyncTask that uses the dependency injection pattern with the interface created in step 1.

This could look something like this (I’ve omitted Exception handling and other things):

Make the AsyncTask use the interface methods

With the above preparations, the DownloadFilesTask’s onPostExecute method should now look like this:

Show me your tests please

The preparations are done, so we can now take a look at the actual unit test for such an AsyncTask:

Of course you should pass something that can be evaluated in downloadCompleted, like the number of bytes downloaded or some custom result code. This is however trivial, so I leave it as an exercise to the reader.

I hope this post will help you design better asynchronous operations that are easily testable and adhere to the clean code principles.

Tags: , , ,

12 Responses to “Testable Android AsyncTask”

  1. İsmail Kocacan 06/01/2014 at 10:56 pm #

    Yeah !

    Nice article  :)

  2. Igor Ganapolsky 26/02/2015 at 11:09 pm #

    Interesting idea.  But now in 2015, do you still recommend using the AsyncTask architecture?  Or perhaps migrate to something more modern like RxJava?

    Thanks,
    Igor

    • Marcelo 17/03/2015 at 6:20 pm #

      Hi Igor, yes you are right, nowadays I would not recommend using AsyncTask. RxJava is an alternative, also I like JDeferred as it feels more natural to me since I use jQuery/AngularJS promises with a similar syntax.

      Cheers,
      Marcelo

  3. Roberto Allende 25/12/2015 at 2:12 am #

    Awesome post!, thank you very much.

Trackbacks/Pingbacks

  1. Unit Testing Performance | Making Software - 02/03/2013

    […] while back I wrote about a strategy for testing AsyncTasks in Android. If you look at the unit test at the bottom of the article, you will notice that there is a 30 […]

  2. Replace AsyncTask and AsyncTaskLoader with rx.Observable – RxJava Android Patterns - stable/kernel Blog - 17/02/2015

    […] difficult without doing something unnatural that’s most likely fragile and/or hard to maintain. Here’s a post talking about some ways to acheive it […]

  3. Replace AsyncTask and AsyncTaskLoader with rx.Observable – RxJava Android Patterns - 17/02/2015

    […] without doing something unnatural that’s most likely fragile and/or hard to maintain. Here’s a post talking about some ways to acheive it […]

  4. 【Android开发经验】用RxJava.Observable取代AsyncTask和AsyncTaskLoader-RxJava Android模版 – 剑客|关注科技互联网 - 30/03/2015

    […] 抛开这些不说,如果你喜欢测试你的代码,AsyncTask并不能给你带来什么帮助。如果我们不做额外的工作,测试AsyncTask非常困难,它很脆弱并且难以维持。这是一篇有关如何成功测试AsyncTask的帖子。 […]

  5. 使用RxJava.Observable取代AsyncTask和AsyncTaskLoader | 猪小乐博客 - 06/04/2015

    […] 抛开这些不说,如果你喜欢测试你的代码,AsyncTask并不能给你带来什么帮助。如果我们不做额外的工作,测试AsyncTask非常困难,它很脆弱并且难以维持。这是一篇有关如何成功测试AsyncTask的帖子。 […]

  6. 使用RxJava.Observable取代AsyncTask和AsyncTaskLoader | 开发技术前线 - 16/04/2015

    […] 抛开这些不说,如果你喜欢测试你的代码,AsyncTask并不能给你带来什么帮助。如果我们不做额外的工作,测试AsyncTask非常困难,它很脆弱并且难以维持。这是一篇有关如何成功测试AsyncTask的帖子。 […]

  7. Wie teste ich eine Methode mit nem AsyncTask und Netzwerkverkehr? - Android-Hilfe.de - 29/04/2015

    […] […]

  8. RxJava 与 AsyncTask | Jacks Blog - 09/05/2015

    […] 难以测试(如何成功测试AyncTask的帖子) […]

Leave a Reply