Tuesday, August 02, 2011

Android Application Testing Guide: Q&A

Lately I've been receiving some comments or questions about some of the book's subjects in my email. To help the community the best I think that is preferable that you share your questions here, as comments to this post if you cannot find a post dealing with the same topic. If possible I will answer also here for the benefit of all.

8 comments:

Pedro Veloso said...

Diego, if we were to test something that is asynchronous, like in my case I'm wanting to test if a webview loads an URL how would I go about waiting for the webpage to finish loading in my webview ?

I've found this while surfing the web : http://code.google.com/p/awaitility/ , would this be a good solution?

Diego Torres Milano said...

Thanks Pedro for your question that will help me improve the book's content.

A method like this would do the trick:


private void assertLoadUrl(String url) {
mWebView.loadUrl(url);
sleep();
assertTrue(!(mWebView.getProgress() < 100));
}


sleep() just calls Thread.sleep().
If you need to detect errors you may need to create a MockWebViewClient.

Diego Torres Milano said...

Detailed answer available in this post.

Pedro Veloso said...

Very complete answers, thanks a lot Diego ;)

Pedro Veloso said...

Hi Diego. I'm trying to use easymock on TelephonyManager like this:

TelephonyManager telman = createMock(TelephonyManager.class);

but I always get this error:

java.lang.IllegalArgumentException: android.telephony.TelephonyManager is not an interface
at java.lang.reflect.Proxy.getProxyClass(Proxy.java:109)


Do you have any idea why this happens or what I'm doing wrong?

Diego Torres Milano said...

Hi Pedro,
You need the Interface to be able to create a mock, so in your case I guess you need


ITelephony telMan = createMock(ITelephony.class);
telMan.call("555-1234");


but unfortunately ITelephony is hidden


/**
* Interface used to interact with the phone. Mostly this is used by the
* TelephonyManager class. A few places are still using this directly.
* Please clean them up if possible and use TelephonyManager insteadl.
*
* {@hide}
*/
interface ITelephony {
...
}


so you won't be able to do this from the SDK.

Chuck Krutsinger said...

Bought the book and read it. Now I'm diving in and trying to apply what I've learned to my first project and I've hit a wall with how to test a method that performs tasks on a worker thread. The jUnit test calls the program but doesn't wait for the other thread to complete before asserting that everything that should happen has happened. Here is the method invoked:

public void mapButtonClicked()
{
String aZipcode = m_View.getZipcodeEntered();
m_View.clearAllMapOverlays();
if (aZipcode.isEmpty())
{
m_View.displayToastMessage(TOAST_NO_ZIP_ENTERED, Toast.LENGTH_SHORT);
}
else
{
m_View.hideZipcodeTextViewSoftKeys();
m_AddContactsToMapTask = new AddContactsToMapTask();
m_AddContactsToMapTask.execute(new String[] { aZipcode }); // <------ here is the AsyncTask that is invoked on a worker thread
}
}

in jUnit my test is structured as:

public void testMapButtonClicked()
{
m_Controller.mapButtonClicked();
// followed by asserts to check the results which get checked before the thread has completed
}

Since nearly all Android apps must perform long running tasks on worker threads, my need to validate that multi-threaded activities perform the actions expected cannot be unique. I have tried:

1) sleep for a few seconds, this is unreliable as the thread may take longer and it also makes the tests run very slowly, a bad thing
2) create a boolean semaphore to indicate completion and loop the test until true - this means that there is code in the app only used for testing, also a bad thing

Your suggestions would be greatly appreciated. Have you seen a good design pattern for testing the results of worker thread activities? Can you point me in a direction or to some resource? I searched and searched the web with no luck so far.

Diego Torres Milano said...

Hi Chuck,
This is a very interesting question. Sometime, depending on the situation I have used what you describe as 1. This gives you the advantage of having an implicit timeout.

In other cases where this solution is not the most appropriate I would recommend using a latch (i.e. CountDownLatch).

Your test would be something like this:

public void testAsyncTaskFinished() throws Throwable {
final AsyncTaskActivity activity = getActivity();
activity.getLatch().await(TIMEOUT, TimeUnit.MILLISECONDS);
assertTrue("finish".equals(activity.getResult()));
}

It's true, this adds some extra code only intended to ease testing but it's minimal. You have to create the accessor for the latch and initialize it when the task is started (i.e. onPreExecute()) and decrement its value when task is finished (i.e. onPostExcecute()).

I will be creating a post detailing this solution because I think it would help other people and formatting here is very confusing.