I want to use Espresso to test web calls. I've made a small test app to see if I can get this to work without using AsyncTask. Upon getting data back, a TextView tells the user whether the call was successful or not. I'm making the web call in a new thread.
In my test, I tried following the AdvancedSynchronizationTest as a pattern to making the test wait until the web request is fulfilled. My implementation, however, doesn't seem to work. My understanding is that the method that I'm overriding is ending before the callback is returned to the main activity. If that's the case, I'm overwriting the wrong class. I've also tried overriding runnable itself (see example below) and even changed the UI update to simply setting a boolean, but with no different outcome. Obviously, Espresso doesn't like my using another thread, but why doesn't it pick up that it should wait until the thread is complete? What would be an alternative without using AsyncTask?
You can see the whole project here: https://github.com/bqmackay/EspressoCustomThreadingResourceExample.
Here is the code in question:
MainActivityTest
public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActivity> {
...
#Override
protected void setUp() throws Exception {
super.setUp();
getActivity();
Runnable runnable = getActivity().getDownloadHelper().getWebCallRunnable();
CountingIdlingResource countingIdlingResource = new CountingIdlingResource("WebCallRunnableCall");
getActivity().getDownloadHelper().setWebCallRunnable(new DecoratedWebCallRunnable(runnable, countingIdlingResource));
registerIdlingResources(countingIdlingResource);
}
public void testThreadRetrieval() throws Exception {
onView(withText("Thread")).perform(click());
assertTrue(getActivity().isDidThreadReturn());
//onView(withText("Thread Retrieved")).check(matches(isDisplayed()));
}
private class DecoratedWebCallRunnable implements Runnable {
private final Runnable realRunnable;
private final CountingIdlingResource countingIdlingResource;
private DecoratedWebCallRunnable(Runnable realRunnable, CountingIdlingResource countingIdlingResource) {
this.realRunnable = realRunnable;
this.countingIdlingResource = countingIdlingResource;
}
#Override
public void run() {
countingIdlingResource.increment();
try {
realRunnable.run();
} finally {
countingIdlingResource.decrement();
}
}
}
DownloadHelper
public class DownloadHelper {
private Runnable webCallRunnable;
private Runnable createWebCallRunnable() {
return new Runnable() {
#Override
public void run() {
HttpClient httpclient = new DefaultHttpClient();
HttpResponse response = null;
try {
response = httpclient.execute(new HttpGet("http://whoismyrepresentative.com/getall_mems.php?zip=84020"));
StatusLine statusLine = response.getStatusLine();
if(statusLine.getStatusCode() == HttpStatus.SC_OK){
ByteArrayOutputStream out = new ByteArrayOutputStream();
response.getEntity().writeTo(out);
out.close();
repsCallbackInterface.onRepsThreadReceived(true);
} else{
//Closes the connection.
response.getEntity().getContent().close();
repsCallbackInterface.onRepsThreadReceived(false);
throw new IOException(statusLine.getReasonPhrase());
}
} catch (IOException e) {
e.printStackTrace();
}
}
};
}
}
MainActivity
public class MainActivity extends Activity implements RepsCallbackInterface {
....
#Override
public void onRepsThreadReceived(final boolean didReceive) {
setDidThreadReturn(true);
runOnUiThread(new Runnable() {
#Override
public void run() {
threadText.setText(didReceive ? "Thread Retrieved" : "Thread Failed");
}
});
}
}
You didn't really provide a question here, so I assume the question is about why this setup is not working as expected.
There are (at least) two reasons:
The assert assertTrue(getActivity().isDidThreadReturn());in testThreadRetrieval() does not wait at all for IdlingResources to be finished. Only ViewInteraction.check() and ViewInteraction.perform() are aware of those and will check IdlingResources before trying to apply the ViewAssertion or ViewAction.
Even if the commented out line after the assertTrue() is applied instead, it would not work, because the instrumentation thread is independent and doesn't wait for the new thread created in getReps() to start, so the check of IdlingResources can and will happen before the CountingIdlingResource is incremented.
If, for whatever reason, you really don't want to use an AsyncTask, you still can profit from Espresso watching the AsynTask thread pool by using its executor. So instead of starting a separate thread, just submit the Runnable to the AsyncTask.THREAD_POOL_EXECUTOR.
Related
I have an Android app from which I receive BLE data (every 62ms via notifications). The app can save data via a BufferedWriter to a file. Upon each onCharacteristicChanged() callback, I call either an AsyncTask, Thread or an IntentService to do a file write if the user enabled file save.
The AsyncTask seems to work fine. But the docs say execute must be invoked on the UI thread, and I'm calling it from the BLE callback. Is that a problem? And how should I fix it?
Using Thread causes this error: GKI_exception out of buffers https://code.google.com/p/android/issues/detail?id=65455 (except my code is not scanning but receiving notifications) and if the file save is long, I need to power cycle the Nexus 7 (the app and BLE become totally unresponsive). Why does the Thread not work and how can I fix it?
The IntentService never goes to the onHandleIntent(). What are the issues here?
Here is some code:
...
_context = this.getApplicationContext();
...
private BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
...
#Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
...
int mode = 1;
if (mode==0) // Asynctask
new doFileWriteTask().execute(strBuild.toString());
else if (mode==1) // Thread
{
final String str = strBuild.toString();
new Thread(new Runnable() {
public void run() {
try {
_writer.write(str);
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
else if (mode==2) // intentService
{
Intent mServiceIntent = new Intent(_context, writeFileService.class);
mServiceIntent.putExtra("foo", strBuild.toString());
startService(mServiceIntent);
}
}
...
};
private class doFileWriteTask extends AsyncTask<String, Void, Void> {
#Override
protected Void doInBackground(String... strings) {
try {
_writer.write(strings[0]);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private class writeFileService extends IntentService {
public writeFileService() {
super("writeFileService");
}
#Override
protected void onHandleIntent(Intent workIntent) {
String dataString = workIntent.getStringExtra("foo");
try {
_writer.write(dataString);
} catch (Exception e) {
e.printStackTrace();
}
}
}
...
But the docs say execute must be invoked on the UI thread, and I'm calling it from the BLE callback. Is that a problem? And how should I fix it?
The framework triggers the AsyncTask callback methods on the same thread it was called from (presumed to be the main thread). It doesn't really affect the background work, but you could see problems if you started trying to use onPostExecute() and the like. AsyncTask probably isn't the best choice to be called from a thread that you don't have control over.
Why does the Thread not work and how can I fix it?
I can't say exactly why you are still seeing errors, through spawning a series of private unsynchronized threads will probably lead to other headaches. If you want to use a single worker thread, a better choice would be to use a single HandlerThread that you can post to from your event callbacks using a Handler, something like:
…
_workerThread = new HandlerThread("Worker");
_workerThread.start();
_handler = new Handler(_workerThread.getLooper(), new Handler.Callback() {
#Override
public boolean handleMessage(Message msg) {
String str = (String) msg.obj;
_writer.write(str);
return true;
}
});
…
#Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
…
Message msg = Message.obtain(_handler, 0, strBuild.toString());
_handler.sendMessage(msg);
…
}
That solution is quite a bit more code, but given the frequency of writes this is probably the most efficient choice.
The IntentService never goes to the onHandleIntent(). What are the issues here?
You should pretty much never implement a top level Android component (activity, service, content provider, receiver) as an inner class, because they have to be declared in your manifest as well (and the XML syntax for inner classes is ugly). If your service does not have a matching entry in the manifest, then you will never see it start. You might want to have a look at the docs on using services.
At a minimum, a Service written as an inner class must be public static to work. Otherwise the framework cannot see it and cannot instantiate it using a default constructor (non-static inner classes mess with the constructor). Unless you are calling startService() inside of a try/catch right now, I'm surprised it isn't crashing when you attempt this.
IntentService is probably the simplest of your three choices because it is the most decoupled and the framework will handle queueing up work and tearing down the threads when all the incoming work is done.
I've been working on an android app which regularly checks a mysql database using JSON and everything works fine with my code.
Im having trouble running this as a timer as it only runs once and then stops.
The only code i managed to get working runs the http request on the UI thread which freezes up.
Any help would be most appreciated.
Thank in advance,
#Override
protected void onCreate(Bundle savedInstanceState) {
...
checkUpdate.start();
...
}
private Thread checkUpdate = new Thread() {
public void run() {
try {
// my code here to get web request to return json string
}
String response = httpclient.execute(httppost, responseHandler);
mHandler.post(showUpdate);
}
...
}
private Runnable showUpdate = new Runnable(){
public void run(){
try{
// my code here handles json string as i need it
Toast.makeText(MainActivity.this,"New Job Received...", Toast.LENGTH_LONG).show();
showja();
}
}
}
private void showja(){
Intent i = new Intent(this, JobAward.class);
i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(i);
finish();
}
As #Raghunandan suggested, the standard way to perform work in the background on Android, and then modify the UI when that work is done, is using AsyncTask.
First define a new subclass of AsyncTask:
private class JsonRequestTask extends AsyncTask<HttpUriRequest, Void, String> {
protected String doInBackground(HttpUriRequest... requests) {
// this code assumes you only make one request at a time, but
// you can easily extend the code to make multiple requests per
// doInBackground() invocation:
HttpUriRequest request = requests[0];
// my code here to get web request to return json string
String response = httpclient.execute(request, responseHandler);
return response;
}
protected void onPostExecute(String jsonResponse) {
// my code here handles json string as i need it
Toast.makeText(MainActivity.this, "New Job Received...", Toast.LENGTH_LONG).show();
showja();
}
}
and then you would use the task like this, instead of your Thread:
#Override
protected void onCreate(Bundle savedInstanceState) {
...
JsonRequestTask task = new JsonRequestTask();
task.execute(httppost);
...
}
You may run the task again by simply creating a new JsonRequestTask() and calling its execute() method.
A common practice for a simple async task like this is to make it a private inner class within the Activity class that uses it (if only one Activity needs it). You may need to change the scope of some of your activity's variables so that the inner class may use them (e.g. move local variables to member variables).
I need to perform a series of http requests, each of which may depend on a previous http response. I have been able to achieve this using an AsyncTask "tree" of sorts, but as the decision tree grows, the AsyncTask technique grows more unwieldy.
I think that somehow using a SynchronousQueue (or other type of queue) is the best approach, but I can't seem to find any good guidance or tutorials on how to use a Queue for something like http requests.
Can anyone provide any guidance or point to any good tutorials on using SynchronousQueue or suggest the best kind of Queue?
Use a java.util.concurrent.SingleThreadExecutor and make a Runnable out of each HTTP operation and result-handler. You can submit subsequent tasks to it as you determine whether you need to continue progress.
For example, the HTTP "task" would run and submit the Result "task" on success, or the Error "task" on failure. The Result task would in-turn submit another HTTP task when it was done processing. Using SingleThreadExecutor ensures only one task runs at-a-time.
You could use a ThreadPoolExecutor if you can handle multiple operations in-flight at once.
Take all that, and wrap it in an AsyncTask that manages the top-level "kick-off" and waits for everything to complete. It would probably be useful to have a ConditionVariable or something to synchronize the "end" signal (using a Done "task") so you can safely tear down the Executor.
A SynchronousQueue doesn't do anything helpful for you here, because it leaves you to do all the tread management. If you use an Executor that is all handled and all you deal with is Runnables and Futures. That's probably why you are not finding any tutorials. Anyway, the Executors all use one of those queue implementations underneath!
As requested, here is some skeleton Java code. Unsupported untested as-is. This should get you started. You can use a different synchronization object if you don't like ConditionVariable.
This is a generic technique, not specific to Android, feel free to use it in other contexts.
This functions as a State Machine, with HttpTask et al forming the states, and the transitions are hard-coded by submitting the Next State to the ExecutorService. There's even a "Big Bang at the end, so everyone knows when to clap" in the form of the ConditionVariable.
Some may consider DoneTask and FailedTask overkill, but it keeps the Next State mechanism consistent, and lets Future<? extends ResultTask> function as a somewhat type-safe container for the results, and certainly keeps you from mis-assigning to it.
abstract class BasicTask {
final ExecutorService es;
final ConditionVariable cv;
public BasicTask(ExecutorService es, ConditionVariable cv) {
this.es = es;
this.cv = cv;
}
}
abstract class HttpTask extends BasicTask {
// source omitted.
// you should make a class to prepare e.g. Apache HTTP resources for specific tasks (see below).
}
abstract class ResultTask implements Runnable {
final ConditionVariable cv;
public ResultTask(ConditionVariable cv) {
this.cv = cv;
}
public void run() {
cv.open();
}
}
final class FailedTask extends ResultTask {
final Exception ex;
public FailedTask(ConditionVariable cv, Exception ex) {
super(cv);
this.ex = ex;
}
public Exception getError() { return ex; }
}
final class DoneTask<T> extends ResultTask {
final T results;
public DoneTask(ConditionVariable cv, T results) {
super(cv);
this.results = results;
}
public T getResults() { return results; }
}
class HttpSequence extends AsyncTask<Void,Void,Object> {
// this will capture the ending task
Future<? extends ResultTask> result;
// this is an inner class, in order to set Result. Refactor so these are small.
// if you don't like inner classes, you still need to arrange for capturing the "answer"
final class SomeHttpTask extends HttpTask implements Runnable {
public void run() {
try {
final SomeType thisStep = doTheStuff(lastStep);
if(thisStep.isDone()) {
// we are done here
result = es.submit(new DoneTask<SomeType>(cv, thisStep));
}
else if(thisStep.isFailed()) {
// not done: we can't proceed because of something in the response
throw thisStep.getError();
}
else {
// not done, everything is ok for next step
es.submit(new NextHttpTask(es, cv, thisStep));
}
}
catch(Exception ex) {
result = es.submit(new FailedTask(cv, ex));
}
}
}
final class TheFirstTask extends HttpTask implements Runnable {
// source omitted. just emphasizing you need one of these for each "step".
// if you don't need to set Result, this could be a static inner class.
}
#Override
public Object doInBackground(Void...) {
final ExecutorService es = Executors.newSingleThreadExecutor();
final ConditionVariable cv = new ConditionVariable(false);
try {
es.submit(new TheFirstTask(es, cv));
// you can choose not to timeout at this level and simply block until something happens...
final boolean done = cv.block(timeout);
if(!done) {
// you will need to account for unfinished threads, see finally section!
return new IllegalStateException("timed out waiting on completion!");
}
if(result != null) {
final ResultTask done = result.get();
if(done instanceof DoneTask) {
// pass SomeType to onPostExecute()
return ((DoneTask<SomeTYpe>)done).getResults();
}
else if(done instanceof FailedTask) {
// pass Exception to onPostExecute()
return ((FailedTask)done).getError();
}
else {
// something bad happened, pass it to onPostExecute()
return new IllegalStateException("something unexpected signalled CV!");
}
}
else {
// something bad happened, pass it to onPostExecute()
return new IllegalStateException("something signalled CV without setting result!");
}
}
catch(Exception ex) {
// something outside workflow failed, pass it to onPostExecute()
return ex;
}
finally {
// naive shutdown (doesn't interrupt running tasks): read JavaDoc on ExecutorService for details
es.shutdown();
}
}
#Override
public void onPostExecute(Object result) {
if(result instanceof SomeType) {
// success UI
}
else if(result instanceof Exception) {
// error UI
}
}
}
I can't say for sure without knowing the details of your use case, but you probably want to avoid the SynchronousQueue, as it will block the thread putting things into the queue until the listener thread takes it back out of the queue. If you were putting things in using the UI thread you'd be locking up the UI.
I think a BlockingQueue may suit your needs. The JavaDoc has a good producer-consumer example.
This question is about event handling on Android. It is not specific to c++.
I need to process UI/OS events, without blocking when all events have been processed.
The reason is that the application I am porting is very large and can't easily be rewritten to deal with its own stuff on a worker thread. Instead the application engine asks for UI/OS events to be processed during long-winded operations that would otherwise be blocking.
I have found that ALooper_pollAll(...) doesn't do this for me. If I, for example, create a dialog in my activity and start a long operation, ALooper_pollAll() won't make my dialog appear - it will show only when I return to the main loop (I tested this in onNativeWindowCreated).
The only solution that I have found to almost work is to do an inner loop on the UI thread, by calling the following code through JNI:
public class MyActivity extends NativeActivity {
private Handler _uiEventsHandler = null;
private Runnable _uiEventsTask = new Runnable() {
public void run() {
Looper looper = Looper.myLooper();
looper.quit();
_uiEventsHandler.removeCallbacks(this);
_uiEventsHandler = null;
}
};
public void ProcessEvents(int timeout)
{
if (_uiEventsHandler==null) {
Looper looper = Looper.myLooper();
_uiEventsHandler = new Handler(looper);
_uiEventsHandler.removeCallbacks(_uiEventsTask);
//_uiEventsHandler.postDelayed(_uiEventsTask,timeout);
_uiEventsHandler.post(_uiEventsTask);
try {
looper.loop();
} catch (RuntimeException re) {
// We get an exception when we try to quit the loop, but the inner loop actually terminates
}
}
}
}
This is, however, not an optimal solution, because it will not loop until there would be no more events to process (because events may be created during the run of the loop).
During my research I have found that I can get the MessageQueue from the Looper and add an IdleHandler that can quit my inner loop. I haven't tried this yet, there has to be a better way.
Given the fact that this is the architecture I must stick with, what is a better solution?
Update:
Using the MessageQueue I'm able to achieve what I need:
public class MyActivity extends NativeActivity {
private class IdleHandler implements MessageQueue.IdleHandler {
private Looper _looper;
protected IdleHandler(Looper looper) {
_looper = looper;
}
public boolean queueIdle() {
_uiEventsHandler = new Handler(_looper);
_uiEventsHandler.post(_uiEventsTask);
return(false);
}
};
private boolean _processingEventsf = false;
private Handler _uiEventsHandler = null;
private Runnable _uiEventsTask = new Runnable() {
public void run() {
Looper looper = Looper.myLooper();
looper.quit();
_uiEventsHandler.removeCallbacks(this);
_uiEventsHandler = null;
}
};
public void ProcessEvents()
{
if (!_processingEventsf) {
Looper looper = Looper.myLooper();
looper.myQueue().addIdleHandler(new IdleHandler(looper));
_processingEventsf = true;
try {
looper.loop();
} catch (RuntimeException re) {
// We get an exception when we try to quit the loop.
}
_processingEventsf = false;
}
}
}
However, I still would like to know if there is a better solution.
Not sure if I understood the question correctly but have you tried using an IntentService?
http://developer.android.com/reference/android/app/IntentService.html
From the docs:
This "work queue processor" pattern is commonly used to offload tasks from an application's main thread. The IntentService class exists to simplify this pattern and take care of the mechanics."
I have a very simple AsyncTask implementation example and am having problem in testing it using Android JUnit framework.
It works just fine when I instantiate and execute it in normal application.
However when it's executed from any of Android Testing framework classes (i.e. AndroidTestCase, ActivityUnitTestCase, ActivityInstrumentationTestCase2 etc) it behaves strangely:
It executes doInBackground() method correctly
However it doesn't invokes any of its notification methods (onPostExecute(), onProgressUpdate(), etc) -- just silently ignores them without showing any errors.
This is very simple AsyncTask example:
package kroz.andcookbook.threads.asynctask;
import android.os.AsyncTask;
import android.util.Log;
import android.widget.ProgressBar;
import android.widget.Toast;
public class AsyncTaskDemo extends AsyncTask<Integer, Integer, String> {
AsyncTaskDemoActivity _parentActivity;
int _counter;
int _maxCount;
public AsyncTaskDemo(AsyncTaskDemoActivity asyncTaskDemoActivity) {
_parentActivity = asyncTaskDemoActivity;
}
#Override
protected void onPreExecute() {
super.onPreExecute();
_parentActivity._progressBar.setVisibility(ProgressBar.VISIBLE);
_parentActivity._progressBar.invalidate();
}
#Override
protected String doInBackground(Integer... params) {
_maxCount = params[0];
for (_counter = 0; _counter <= _maxCount; _counter++) {
try {
Thread.sleep(1000);
publishProgress(_counter);
} catch (InterruptedException e) {
// Ignore
}
}
}
#Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
int progress = values[0];
String progressStr = "Counting " + progress + " out of " + _maxCount;
_parentActivity._textView.setText(progressStr);
_parentActivity._textView.invalidate();
}
#Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
_parentActivity._progressBar.setVisibility(ProgressBar.INVISIBLE);
_parentActivity._progressBar.invalidate();
}
#Override
protected void onCancelled() {
super.onCancelled();
_parentActivity._textView.setText("Request to cancel AsyncTask");
}
}
This is a test case. Here AsyncTaskDemoActivity is a very simple Activity providing UI for testing AsyncTask in mode:
package kroz.andcookbook.test.threads.asynctask;
import java.util.concurrent.ExecutionException;
import kroz.andcookbook.R;
import kroz.andcookbook.threads.asynctask.AsyncTaskDemo;
import kroz.andcookbook.threads.asynctask.AsyncTaskDemoActivity;
import android.content.Intent;
import android.test.ActivityUnitTestCase;
import android.widget.Button;
public class AsyncTaskDemoTest2 extends ActivityUnitTestCase<AsyncTaskDemoActivity> {
AsyncTaskDemo _atask;
private Intent _startIntent;
public AsyncTaskDemoTest2() {
super(AsyncTaskDemoActivity.class);
}
protected void setUp() throws Exception {
super.setUp();
_startIntent = new Intent(Intent.ACTION_MAIN);
}
protected void tearDown() throws Exception {
super.tearDown();
}
public final void testExecute() {
startActivity(_startIntent, null, null);
Button btnStart = (Button) getActivity().findViewById(R.id.Button01);
btnStart.performClick();
assertNotNull(getActivity());
}
}
All this code is working just fine, except the fact that AsyncTask doesn't invoke it's notification methods when executed by within Android Testing Framework. Any ideas?
I met a similar problem while implementing some unit-test. I had to test some service which worked with Executors, and I needed to have my service callbacks sync-ed with the test methods from my ApplicationTestCase classes. Usually the test method itself finished before the callback would be accessed, so the data sent via the callbacks would not be tested. Tried applying the #UiThreadTest bust still didn't work.
I found the following method, which worked, and I still use it. I simply use CountDownLatch signal objects to implement the wait-notify (you can use synchronized(lock){... lock.notify();}, however this results in ugly code) mechanism.
public void testSomething(){
final CountDownLatch signal = new CountDownLatch(1);
Service.doSomething(new Callback() {
#Override
public void onResponse(){
// test response data
// assertEquals(..
// assertTrue(..
// etc
signal.countDown();// notify the count down latch
}
});
signal.await();// wait for callback
}
I found a lot of close answers but none of them put all the parts together correctly. So this is one correct implementation when using an android.os.AsyncTask in your JUnit tests cases.
/**
* This demonstrates how to test AsyncTasks in android JUnit. Below I used
* an in line implementation of a asyncTask, but in real life you would want
* to replace that with some task in your application.
* #throws Throwable
*/
public void testSomeAsynTask () 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) {
//Do something meaningful.
return "something happened!";
}
#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("Happiness", true);
}
The way to deal with this is to run any code that invokes an AsyncTask in runTestOnUiThread():
public final void testExecute() {
startActivity(_startIntent, null, null);
runTestOnUiThread(new Runnable() {
public void run() {
Button btnStart = (Button) getActivity().findViewById(R.id.Button01);
btnStart.performClick();
}
});
assertNotNull(getActivity());
// To wait for the AsyncTask to complete, you can safely call get() from the test thread
getActivity()._myAsyncTask.get();
assertTrue(asyncTaskRanCorrectly());
}
By default junit runs tests in a separate thread than the main application UI. AsyncTask's documentation says that the task instance and the call to execute() must be on the main UI thread; this is because AsyncTask depends on the main thread's Looper and MessageQueue for its internal handler to work properly.
NOTE:
I previously recommended using #UiThreadTest as a decorator on the test method to force the test to run on the main thread, but this isn't quite right for testing an AsyncTask because while your test method is running on the main thread no messages are processed on the main MessageQueue — including the messages the AsyncTask sends about its progress, causing your test to hang.
If you don't mind executing the AsyncTask in the caller thread (should be fine in case of Unit testing), you can use an Executor in the current thread as described in https://stackoverflow.com/a/6583868/1266123
public class CurrentThreadExecutor implements Executor {
public void execute(Runnable r) {
r.run();
}
}
And then you run your AsyncTask in your unit test like this
myAsyncTask.executeOnExecutor(new CurrentThreadExecutor(), testParam);
This is only working for HoneyComb and higher.
I wrote enough unitests for Android and just want to share how to do that.
First off, here is helper class that responsible to wait and release waiter. Nothing special:
SyncronizeTalker
public class SyncronizeTalker {
public void doWait(long l){
synchronized(this){
try {
this.wait(l);
} catch(InterruptedException e) {
}
}
}
public void doNotify() {
synchronized(this) {
this.notify();
}
}
public void doWait() {
synchronized(this){
try {
this.wait();
} catch(InterruptedException e) {
}
}
}
}
Next, lets create interface with one method that should be called from AsyncTask when work is done. Sure we also want to test our results:
TestTaskItf
public interface TestTaskItf {
public void onDone(ArrayList<Integer> list); // dummy data
}
Next lets create some skeleton of our Task that we gonna test:
public class SomeTask extends AsyncTask<Void, Void, SomeItem> {
private ArrayList<Integer> data = new ArrayList<Integer>();
private WmTestTaskItf mInter = null;// for tests only
public WmBuildGroupsTask(Context context, WmTestTaskItf inter) {
super();
this.mContext = context;
this.mInter = inter;
}
#Override
protected SomeItem doInBackground(Void... params) { /* .... job ... */}
#Override
protected void onPostExecute(SomeItem item) {
// ....
if(this.mInter != null){ // aka test mode
this.mInter.onDone(data); // tell to unitest that we finished
}
}
}
At last - our unitest class:
TestBuildGroupTask
public class TestBuildGroupTask extends AndroidTestCase implements WmTestTaskItf{
private SyncronizeTalker async = null;
public void setUP() throws Exception{
super.setUp();
}
public void tearDown() throws Exception{
super.tearDown();
}
public void test____Run(){
mContext = getContext();
assertNotNull(mContext);
async = new SyncronizeTalker();
WmTestTaskItf me = this;
SomeTask task = new SomeTask(mContext, me);
task.execute();
async.doWait(); // <--- wait till "async.doNotify()" is called
}
#Override
public void onDone(ArrayList<Integer> list) {
assertNotNull(list);
// run other validations here
async.doNotify(); // release "async.doWait()" (on this step the unitest is finished)
}
}
That's all.
Hope it will help to someone.
This can be used if you want to test the result from the doInBackground method. Override the onPostExecute method and perform the tests there. To wait for the AsyncTask to complete use CountDownLatch. The latch.await() waits till the countdown runs from 1 (which is set during initialization) to 0 (which is done by the countdown() method).
#RunWith(AndroidJUnit4.class)
public class EndpointsAsyncTaskTest {
Context context;
#Test
public void testVerifyJoke() throws InterruptedException {
assertTrue(true);
final CountDownLatch latch = new CountDownLatch(1);
context = InstrumentationRegistry.getContext();
EndpointsAsyncTask testTask = new EndpointsAsyncTask() {
#Override
protected void onPostExecute(String result) {
assertNotNull(result);
if (result != null){
assertTrue(result.length() > 0);
latch.countDown();
}
}
};
testTask.execute(context);
latch.await();
}
How about using join?
fun myTest() = runBlocking {
CoroutineScope(Dispatchers.IO).launch {
// test something here
}.join()
}
Use this simple solution
runBlocking{
//Your code here
}
Most of those solutions require a lot of code to be written for every test or to change your class structure. Which I find very difficult to use if you have many situations under test or many AsyncTasks on your project.
There is a library which eases the process of testing AsyncTask. Example:
#Test
public void makeGETRequest(){
...
myAsyncTaskInstance.execute(...);
AsyncTaskTest.build(myAsyncTaskInstance).
run(new AsyncTest() {
#Override
public void test(Object result) {
Assert.assertEquals(200, (Integer)result);
}
});
}
}
Basically, it runs your AsyncTask and test the result it returns after the postComplete() has been called.