我有一个非常简单的AsyncTask实现示例,并且在使用Android JUnit框架测试它时遇到问题.
当我在普通应用程序中实例化并执行它时,它工作得很好.但是当它从任何Android测试框架类(即AndroidTestCase,ActivityUnitTestCase,ActivityInstrumentationTestCase2等)执行时,它表现得很奇怪:
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");
}
}
Run Code Online (Sandbox Code Playgroud)
这是一个测试用例.这里AsyncTaskDemoActivity是一个非常简单的Activity,提供用于在模式下测试AsyncTask的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());
}
}
Run Code Online (Sandbox Code Playgroud)
所有这些代码都运行得很好,除了AsyncTask在Android Testing Framework中执行时不调用它的通知方法.有任何想法吗?
小智 124
我在实现一些单元测试时遇到了类似的问题.我必须测试一些与Executors一起工作的服务,我需要让我的服务回调与我的ApplicationTestCase类中的测试方法同步.通常,测试方法本身在访问回调之前就已完成,因此不会测试通过回调发送的数据.尝试应用@UiThreadTest胸围仍然无法正常工作.
我找到了以下方法,该方法有效,我仍然使用它.我只是使用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
}
Run Code Online (Sandbox Code Playgroud)
Bil*_*een 92
我找到了很多很接近的答案,但没有一个能正确地将所有部分放在一起.因此,在JUnit测试用例中使用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);
}
Run Code Online (Sandbox Code Playgroud)
Ale*_*lav 25
处理此问题的方法是运行调用AsyncTask的任何代码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());
}
Run Code Online (Sandbox Code Playgroud)
默认情况下,junit在与主应用程序UI不同的单独线程中运行测试.AsyncTask的文档说任务实例和对execute()的调用必须在主UI线程上; 这是因为AsyncTask依赖于主线程Looper并且MessageQueue内部处理程序可以正常工作.
我以前建议@UiThreadTest在测试方法上使用它作为装饰器来强制测试在主线程上运行,但这对于测试AsyncTask不太正确,因为当你的测试方法在主线程上运行时,没有消息在main MessageQueue - 包括AsyncTask发送的有关其进度的消息,导致测试挂起.
小智 5
如果您不介意在调用程序线程中执行AsyncTask(在单元测试的情况下应该没问题),您可以在当前线程中使用Executor,如/sf/answers/460870791/中所述
public class CurrentThreadExecutor implements Executor {
public void execute(Runnable r) {
r.run();
}
}
Run Code Online (Sandbox Code Playgroud)
然后在这样的单元测试中运行AsyncTask
myAsyncTask.executeOnExecutor(new CurrentThreadExecutor(), testParam);
Run Code Online (Sandbox Code Playgroud)
这仅适用于HoneyComb及更高版本.
我为Android编写了足够多的unitests,只想分享如何做到这一点.
首先,这里是负责等待和释放服务员的助手类.没什么特别的:
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) {
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
接下来,让我们使用一个应该AsyncTask在工作完成时调用的方法创建接口.当然,我们也想测试我们的结果:
TestTaskItf
public interface TestTaskItf {
public void onDone(ArrayList<Integer> list); // dummy data
}
Run Code Online (Sandbox Code Playgroud)
接下来让我们创建一个我们要测试的Task的骨架:
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
}
}
}
Run Code Online (Sandbox Code Playgroud)
最后 - 我们最潇洒的课程:
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)
}
}
Run Code Online (Sandbox Code Playgroud)
就这样.
希望它对某人有所帮助.
| 归档时间: |
|
| 查看次数: |
55678 次 |
| 最近记录: |