Ich habe ein sehr einfaches AsyncTask-Implementierungsbeispiel und habe Probleme beim Testen mit dem Android JUnit-Framework.
Es funktioniert einwandfrei, wenn ich es in einer normalen Anwendung instanziiere und ausführe. Wenn es jedoch von einer der Android Testing-Framework-Klassen (z. B. AndroidTestCase , ActivityUnitTestCase , ActivityInstrumentationTestCase2 usw.) ausgeführt wird, verhält es sich seltsam:
- Die
doInBackground()
Methode wird korrekt ausgeführt - Allerdings ist es nicht eine ihrer Benachrichtigungsmethoden aufruft (
onPostExecute()
,onProgressUpdate()
usw.) - nur still ignoriert sie ohne Fehler zeigt.
Dies ist ein sehr einfaches AsyncTask-Beispiel:
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");
}
}
Dies ist ein Testfall. Hier ist AsyncTaskDemoActivity eine sehr einfache Aktivität, die eine Benutzeroberfläche zum Testen von AsyncTask im Modus bereitstellt:
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());
}
}
Der gesamte Code funktioniert einwandfrei, mit Ausnahme der Tatsache, dass AsyncTask seine Benachrichtigungsmethoden nicht aufruft, wenn es von Android Testing Framework ausgeführt wird. Irgendwelche Ideen?
Service.doSomething()
?task.execute(Param...)
vorawait()
und setzencountDown()
inonPostExecute(Result)
? (siehe stackoverflow.com/a/5722193/253468 ) Auch @PeterAjtaiService.doSomething
ist ein asynchroner Aufruf wietask.execute
.Service.doSomething()
Hier sollten Sie Ihren Service- / Async-Task-Aufruf ersetzen. Stellen Sie sicher,signal.countDown()
dass Sie jede Methode aufrufen , die Sie implementieren müssen, sonst bleibt Ihr Test hängen.Ich habe viele enge Antworten gefunden, aber keine hat alle Teile richtig zusammengefügt. Dies ist also eine korrekte Implementierung, wenn Sie eine android.os.AsyncTask in Ihren JUnit-Testfällen verwenden.
quelle
assertTrue(signal.await(...));
Der Weg, um damit umzugehen, besteht darin, jeden Code auszuführen, der eine AsyncTask aufruft in
runTestOnUiThread()
:Standardmäßig führt junit Tests in einem anderen Thread als der Benutzeroberfläche der Hauptanwendung aus. In der Dokumentation von AsyncTask heißt es, dass sich die Taskinstanz und der Aufruf von execute () im Haupt-UI-Thread befinden müssen. Dies liegt daran, dass AsyncTask vom Hauptthread abhängt
Looper
undMessageQueue
dass sein interner Handler ordnungsgemäß funktioniert.HINWEIS:
Ich habe zuvor empfohlen,
@UiThreadTest
die Testmethode als Dekorator zu verwenden, um zu erzwingen, dass der Test auf dem Hauptthread ausgeführt wird. Dies ist jedoch nicht ganz richtig, um eine AsyncTask zu testen, da während die Testmethode auf dem Hauptthread ausgeführt wird, keine Nachrichten auf dem Test verarbeitet werden main MessageQueue - einschließlich der Nachrichten, die die AsyncTask über ihren Fortschritt sendet, wodurch Ihr Test hängen bleibt.quelle
runTestOnUiThread()
eine Testmethode mit dem@UiThreadTest
Dekorateur verwendet? Das wird nicht funktionieren. Wenn eine Testmethode nicht vorhanden ist@UiThreadTest
, sollte sie standardmäßig auf einem eigenen Nicht-Hauptthread ausgeführt werden.Deprecated in API level 24
developer.android.com/reference/android/test/… anInstrumentationRegistry.getInstrumentation().runOnMainSync()
stattdessen verwenden!Wenn es Ihnen nichts ausmacht, die AsyncTask im Aufruferthread auszuführen (sollte im Falle von Unit-Tests in Ordnung sein), können Sie im aktuellen Thread einen Executor verwenden, wie unter https://stackoverflow.com/a/6583868/1266123 beschrieben
Und dann führen Sie Ihre AsyncTask in Ihrem Unit-Test so aus
Dies funktioniert nur für HoneyComb und höher.
quelle
Ich habe genug Unitests für Android geschrieben und möchte nur mitteilen, wie das geht.
Zunächst einmal ist hier die Helferklasse, die dafür verantwortlich ist, zu warten und den Kellner freizulassen. Nichts Besonderes:
SyncronizeTalker
Als nächstes erstellen wir eine Schnittstelle mit einer Methode, von der aus aufgerufen werden soll
AsyncTask
wenn die Arbeit erledigt ist. Natürlich wollen wir auch unsere Ergebnisse testen:TestTaskItf
Als nächstes erstellen wir ein Skelett unserer Aufgabe, das wir testen werden:
Endlich - unsere einheitlichste Klasse:
TestBuildGroupTask
Das ist alles.
Hoffe es wird jemandem helfen.
quelle
Dies kann verwendet werden, wenn Sie das Ergebnis der
doInBackground
Methode testen möchten . Überschreiben Sie dieonPostExecute
Methode und führen Sie die Tests dort durch. Verwenden Sie CountDownLatch, um auf den Abschluss der AsyncTask zu warten. Daslatch.await()
Warten, bis der Countdown von 1 (der während der Initialisierung eingestellt wird) bis 0 (was von dercountdown()
Methode durchgeführt wird) läuft .quelle
Die meisten dieser Lösungen erfordern, dass für jeden Test viel Code geschrieben oder Ihre Klassenstruktur geändert wird. Was ich sehr schwierig finde, wenn Sie viele zu testende Situationen oder viele AsyncTasks in Ihrem Projekt haben.
Es gibt eine Bibliothek , die den Testprozess vereinfacht
AsyncTask
. Beispiel:Grundsätzlich wird Ihre AsyncTask ausgeführt und das Ergebnis getestet, das nach dem
postComplete()
Aufruf von zurückgegeben wird.quelle