У меня есть очень простой пример реализации AsyncTask, и у меня возникли проблемы с его тестированием с использованием платформы Android JUnit.
Он отлично работает, когда я создаю и выполняю его в обычном приложении. Однако, когда он выполняется из любого из классов платформы Android Testing (например, 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");
}
}
Это тестовый пример. Здесь AsyncTaskDemoActivity - это очень простое действие, обеспечивающее пользовательский интерфейс для тестирования AsyncTask в режиме:
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());
}
}
Весь этот код работает нормально, за исключением того факта, что AsyncTask не вызывает методы уведомления при выполнении в Android Testing Framework. Любые идеи?
Service.doSomething()
?task.execute(Param...)
прежде чемawait()
и положитьcountDown()
вonPostExecute(Result)
? (см. stackoverflow.com/a/5722193/253468 ) Также @PeterAjtaiService.doSomething
- это асинхронный вызов, напримерtask.execute
.Service.doSomething()
здесь вы должны заменить вызов задачи service / async. Обязательно вызовитеsignal.countDown()
любой метод, который вам нужно реализовать, иначе ваш тест застрянет.Я нашел много близких ответов, но ни один из них не соединил все части правильно. Итак, это одна из правильных реализаций при использовании android.os.AsyncTask в ваших тестовых примерах JUnit.
источник
assertTrue(signal.await(...));
Способ справиться с этим - запустить любой код, который вызывает AsyncTask в
runTestOnUiThread()
:По умолчанию junit запускает тесты в отдельном потоке, чем основной пользовательский интерфейс приложения. В документации AsyncTask говорится, что экземпляр задачи и вызов execute () должны находиться в основном потоке пользовательского интерфейса; это потому, что AsyncTask зависит от основного потока
Looper
иMessageQueue
его внутреннего обработчика для правильной работы.НОТА:
Ранее я рекомендовал использовать
@UiThreadTest
в качестве декоратора для метода тестирования, чтобы заставить тест запускаться в основном потоке, но это не совсем подходит для тестирования AsyncTask, потому что пока ваш метод тестирования выполняется в основном потоке, сообщения не обрабатываются в main MessageQueue - включая сообщения, которые AsyncTask отправляет о своем ходе, что приводит к зависанию вашего теста.источник
runTestOnUiThread()
тестовый метод с@UiThreadTest
декоратором? Это не сработает. Если тестовый метод не имеет@UiThreadTest
, по умолчанию он должен работать в собственном неосновном потоке.Deprecated in API level 24
InstrumentationRegistry.getInstrumentation().runOnMainSync()
вместо этого!Если вы не возражаете против выполнения AsyncTask в потоке вызывающего абонента (должно быть нормально в случае модульного тестирования), вы можете использовать Executor в текущем потоке, как описано в https://stackoverflow.com/a/6583868/1266123
А затем вы запускаете свою AsyncTask в своем модульном тесте следующим образом
Это работает только для HoneyComb и выше.
источник
Я написал достаточно юнитов для Android и просто хочу поделиться, как это сделать.
Во-первых, вот вспомогательный класс, который отвечает за ожидание и освобождение официанта. Ничего особенного:
SyncronizeTalker
Затем давайте создадим интерфейс с одним методом, который должен вызываться по
AsyncTask
завершении работы. Конечно, мы также хотим проверить наши результаты:TestTaskItf
Теперь давайте создадим скелет нашей задачи, который мы собираемся протестировать:
Наконец, наш самый юнит-класс:
TestBuildGroupTask
Вот и все.
Надеюсь, это кому-то поможет.
источник
Это можно использовать, если вы хотите проверить результат
doInBackground
метода. ПереопределитеonPostExecute
метод и проведите там тесты. Чтобы дождаться завершения AsyncTask, используйте CountDownLatch. Вlatch.await()
жду , пока обратный отсчет времени проходит от 1 (который устанавливается во время инициализации) до 0 (что делается с помощьюcountdown()
метода).источник
Большинство этих решений требует написания большого количества кода для каждого теста или для изменения структуры вашего класса. Что мне очень сложно использовать, если у вас много тестируемых ситуаций или много AsyncTasks в вашем проекте.
Есть библиотека, которая облегчает процесс тестирования
AsyncTask
. Пример:По сути, он запускает вашу AsyncTask и проверяет результат, возвращаемый после вызова
postComplete()
.источник