使用Androidtesting框架进行Android AsyncTasktesting

我有一个非常简单的AsyncTask实现示例,并且在使用Android JUnit框架testing它时遇到问题。

当我在正常的应用程序中实例化并执行它时,它工作得很好。 然而,当它从任何Androidtesting框架类(即AndroidTestCaseActivityUnitTestCaseActivityInstrumentationTestCase2等)执行它奇怪的行为: – 它正确执行doInBackground()方法 – 但它不会调用任何通知方法( onPostExecute()onProgressUpdate ()等) – 只是默默地忽略它们而不显示任何错误。

这是非常简单的AsyncTask示例:

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"); } } 

这是一个testing案例。 这里AsyncTaskDemoActivity是一个非常简单的Activity,提供用于在模式下testingAsyncTask的UI:

 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()); } } 

所有这些代码工作得很好,除了在Android Testing Framework中执行时AsyncTask不调用通知方法。 有任何想法吗?

我在实施一些unit testing时遇到了类似的问题。 我必须testing一些与Executors一起工作的服务,我需要将我的服务callback与ApplicationTestCase类中的testing方法同步。 通常testing方法本身在callback被访问之前完成,因此通过callback发送的数据将不被testing。 尝试应用@UThreadTest胸围仍然没有工作。

我发现下面的方法,工作,我仍然使用它。 我只是简单地使用CountDownLatch信号对象来实现wait-notify(你可以使用synchronized(lock){… lock.notify();},但是这会导致丑陋的代码)机制。

 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 } 

我发现了很多相近的答案,但是没有一个能够正确地把所有的部分放在一起。 所以这是在你的JUnittesting用例中使用android.os.AsyncTask时的一个正确实现。

  /** * 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); } 

处理这个问题的方法是运行任何在runTestOnUiThread()中调用AsyncTask的代码:

 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()); } 

默认情况下,junit在与主应用程序UI不同的线程中运行testing。 AsyncTask的文档说任务实例和对execute()的调用必须在主UI线程上; 这是因为AsyncTask依靠主线程的LooperMessageQueue来使其内部处理程序正常工作。

注意:

我以前build议使用@UiThreadTest作为testing方法上的装饰器来强制testing在主线程上运行,但是这不太适合testingAsyncTask,因为当您的testing方法在主线程上运行时,没有消息处理在主要MessageQueue上 – 包括AsyncTask发送的关于其进度的消息,导致您的testing挂起。

如果你不介意在调用者线程中执行AsyncTask(在unit testing的情况下应该没问题),你可以在当前线程中使用一个执行程序,如https://stackoverflow.com/a/6583868/1266123

 public class CurrentThreadExecutor implements Executor { public void execute(Runnable r) { r.run(); } } 

然后在你的unit testing中像这样运行你的AsyncTask

 myAsyncTask.executeOnExecutor(new CurrentThreadExecutor(), testParam); 

这只适用于HoneyComb及更高版本。

我为Android写了足够多的unitest,只是想分享如何做到这一点。

首先,这里是负责等待和释放服务员的辅助class。 没什么特别的:

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) { } } } } 

接下来,让我们创build一个接口,当工作完成后,应该从AsyncTask调用一个方法。 当然我们也想testing我们的结果:

TestTaskItf

 public interface TestTaskItf { public void onDone(ArrayList<Integer> list); // dummy data } 

接下来让我们创build一些我们要testing的任务框架:

 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 } } } 

最后 – 我们一stream的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) } } 

就这样。

希望这对别人有帮助。

如果您想要testingdoInBackground方法的结果,可以使用它。 覆盖onPostExecute方法并在那里执行testing。 等待AsyncTask完成使用CountDownLatch。 latch.await()等待直到倒计数从1(在初始化期间设置)到0(这是由countdown()方法)完成。

 @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(); } 

这些解决scheme大多需要为每个testing编写大量的代码或者改变你的类结构。 如果你有很多被testing的情况,或者你的项目有很多的AsyncTasks,我觉得这很难使用。

有一个库( https://github.com/lsoaresesilva/asynctasktest )可以简化testingAsyncTask的过程。 例:

 @Test public void makeGETRequest(){ ... myAsyncTaskInstance.execute(...); AsyncTaskTest.build(myAsyncTaskInstance). run(new AsyncTest() { @Override public void test(Object result) { Assert.assertEquals(200, (Integer)result); } }); } } 

基本上,它运行你的AsyncTask并testingpostComplete被调用后返回的结果。