Showing posts with label async. Show all posts
Showing posts with label async. Show all posts

Friday, July 26, 2013

Asynchronous code testing in Android

Testing asynchronous calls it is not always an easy task.

Let's take as reference this simple interface:


The first thing is: how can we test it through an Android unit test class?


We can't test the value inside the callback. Infact, an async call runs usually in worker thread. When the thread has done some computation, it calls the callback. The simplest case is when the callback is run in the same worker thread.

But still, we need to find a way to block the main thread, otherwise the test class will simply don't wait before the callback is called! Remember that the method testDoSync() is called in the main thread.

We can use a semaphore, and make the test method wait until the callback is delivered. Then we can easily test the return value. The Semaphore class has some interesting properties: first, we can use it to put a thread in wait; the acquire() method stops the thread until a resource is available. As we have created the Semaphore with 0 resources available, this will cause the next call to acquire() to stop the thread.
But what if there is the unlikely case that the listener, returns before the acquire() call is made? No worries, as the signal() will simply increment the number of resources available and so the acquire() method will not nlock but will return straight away. Yay, this is for sure a better way to handle threads synchronization that the usual pattern wait-notify!
This is the code that covers this case:


And now, the worst case. Let's imagine that the code run by the worker thread do some stuff, and then it posts the results of its work in the calling thread (for instance through a handler). Then, onValueChanged() will never be called. Why? It is because we needed to stop the main thread. The main thread is a looper thread: normally a thread do some stuff and then it dies. Instead, a thread with a looper processes events by looping continuously. Another thread can post events on it: the events are put in the thread queue and they are processed one at time. If we block the main thread, the event posted by the worker thread will NEVER get processed.
How to handle this case?
We still need to block the main thread. But we need to launch the callback from a secondary thread with the looper.
We will use a facility offered by android: HandlerThread.

It will create a thread with looper for us; then the callback will be called in the secondary thread looper that is not blocked, and from that thread we can release the semaphore of the main thread.
Finally, remember to quit() the thread handler (that will quit the embedded looper too and will cause the death of that thread).

Saturday, May 12, 2012

TDD - Testing AsyncTasks on Android

Testing functionalities of a software system is probably one of the most important things to do if you want to release a reliable application to your clients.

TDD (aka Test Driven Development) is a methodology where you create and develop unit tests before writing a single line of application logic. This strategy allows you to better understand the problem your app is solving, it gives you a more pragmatic and structured way to build a software component. It forces you to design and think to the solution in detail before coding it. And, most important, when you change something in your code, you can safely run your tests again and have an immediate result if your changes have affected the behavior of the component.

I usually do TDD to test model classes, database classes and networking. I'm not writing functional tests on the UI but Android offers you tools like monkey to test also user clicks on your UI.

If your code use AysncTasks to perform networking operations in a background thread, things become a little trickier.

Normally, in a AysncTask, you can specify a result of this task using onPostExecute.
This call is executed on the main thread so you can update your UI and notify the user easily.

AsyncTask runs your task in a background thread, but this opens some issues: our tests must be run on the main thread! We need to wait for the AsyncTask to finish before jumping to the next test or to quit the testing routine.
There is a method called runTestOnUiThread that perform a testcase on a main thread. To use such method, your class must extend InstrumentationTestCase.





In the previous code snippet, I'm creating a unit test called testGetMedia whose purpose is doing some networking in an asynchronous fashion, to fetch some data from a remote backend.
The tested class is called NetworkTasks and it contains a method called getMedia(Media) similar to this one:



GetAsyncMediaTask is a subclass of AsyncTask that implements the doInBackground() and the onPostExecute() methods. I'm not showing the code of GetAsyncMediaTask here because is not really interesting for our purpose. In the doInBackground() method an http call is placed and it blocks the calling thread until it gets an HttpResponse; inside the onPostExecute() method, I'm using the listener passed to the NetworkTasks object to notify the component that has launched it. The component is notified through onFail and onSuccess callbacks.
In the code snippet the listener is called GetMediaListener.
This covers almost everything except for few (important!) details: what happens in the GetMediaListener?




The GetMediaListener extends a generic CallbackListener which in turn implements a simple interface with two methods (onFail and onSuccess).
The callbacks are here used to assert that a condition holds or doesn't hold. The listener used in the real code will do some useful stuff on the UI, trigger some notifications and so on.
And here comes into play the CountDownLatch object. It is absolutely mandatory to use this object when you want to test an AsyncTask. Infact, even if we're running the task on the main thread, as soon as the doInBackground method ends, the testGetMedia() method ends too, and there is no way for the listener to be invoked by the system! By using a CountDownLatch, we impose the current thread to stop and wait until a certain condition is met: here we wait that the callback onPostExecute (which in turns call onFail or onSuccess) is executed: as soon as it is executed, the object is decremented with the countdown() method of the supreclass and the lock is removed. The lock is automatically removed after 30 seconds by the signal.await() call.

You can get all the code here: https://gist.github.com/2667108

Enjoy async TDD on Android! :)