J'ai un exemple d'implémentation AsyncTask très simple et j'ai des problèmes pour le tester à l'aide du framework Android JUnit.
Cela fonctionne très bien lorsque je l'instancie et l'exécute dans une application normale. Cependant, lorsqu'il est exécuté à partir de l'une des classes du framework de test Android (par exemple, AndroidTestCase , ActivityUnitTestCase , ActivityInstrumentationTestCase2, etc.), il se comporte étrangement:
- Il exécute
doInBackground()
correctement la méthode - Cependant , il n'invoque l' une de ses méthodes de notification (
onPostExecute()
,onProgressUpdate()
, etc.) - silencieusement les ignore sans montrer aucune erreur.
Ceci est un exemple AsyncTask très simple:
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");
}
}
Ceci est un cas de test. Ici, AsyncTaskDemoActivity est une activité très simple fournissant une interface utilisateur pour tester AsyncTask en 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());
}
}
Tout ce code fonctionne très bien, sauf le fait qu'AsyncTask n'invoque pas ses méthodes de notification lorsqu'il est exécuté dans Android Testing Framework. Des idées?
Service.doSomething()
?task.execute(Param...)
avantawait()
et mettrecountDown()
enonPostExecute(Result)
? (voir stackoverflow.com/a/5722193/253468 ) @PeterAjtai,Service.doSomething
est également un appel async commetask.execute
.Service.doSomething()
est l'endroit où vous devez remplacer votre appel de tâche de service / asynchrone. Assurez-vous d'appelersignal.countDown()
n'importe quelle méthode que vous devez implémenter, sinon votre test restera bloqué.J'ai trouvé beaucoup de réponses proches, mais aucune n'a réuni correctement toutes les parties. Il s'agit donc d'une implémentation correcte lors de l'utilisation d'un android.os.AsyncTask dans vos cas de tests JUnit.
la source
assertTrue(signal.await(...));
La façon de gérer cela est d'exécuter n'importe quel code qui appelle une AsyncTask dans
runTestOnUiThread()
:Par défaut, junit exécute les tests dans un thread distinct de l'interface utilisateur principale de l'application. La documentation d'AsyncTask indique que l'instance de tâche et l'appel à execute () doivent être sur le thread d'interface utilisateur principal; en effet, AsyncTask dépend du thread principal
Looper
etMessageQueue
du bon fonctionnement de son gestionnaire interne.REMARQUE:
J'ai précédemment recommandé d'utiliser
@UiThreadTest
comme décorateur sur la méthode de test pour forcer le test à s'exécuter sur le thread principal, mais ce n'est pas tout à fait correct pour tester une AsyncTask car pendant que votre méthode de test s'exécute sur le thread principal, aucun message n'est traité sur le Main MessageQueue - y compris les messages envoyés par AsyncTask sur sa progression, provoquant le blocage de votre test.la source
runTestOnUiThread()
partir d'une méthode de test avec le@UiThreadTest
décorateur? Cela ne fonctionnera pas. Si une méthode de test n'en a pas@UiThreadTest
, elle doit s'exécuter par défaut sur son propre thread non principal.Deprecated in API level 24
developer.android.com/reference/android/test/…InstrumentationRegistry.getInstrumentation().runOnMainSync()
plutôt!Si cela ne vous dérange pas d'exécuter AsyncTask dans le thread de l'appelant (cela devrait convenir en cas de test unitaire), vous pouvez utiliser un Executor dans le thread actuel comme décrit dans https://stackoverflow.com/a/6583868/1266123
Et puis vous exécutez votre AsyncTask dans votre test unitaire comme ceci
Cela ne fonctionne que pour HoneyComb et supérieur.
la source
J'ai écrit suffisamment de tests unitaires pour Android et je veux juste partager comment faire cela.
Tout d'abord, voici la classe d'assistance chargée d'attendre et de libérer le serveur. Rien de spécial:
SyncronizeTalker
Ensuite, créons une interface avec une méthode qui devrait être appelée à partir du
AsyncTask
moment où le travail est terminé. Bien sûr, nous voulons également tester nos résultats:TestTaskItf
Ensuite, créons un squelette de notre tâche que nous allons tester:
Enfin - notre classe unitaire:
TestBuildGroupTask
C'est tout.
J'espère que cela aidera quelqu'un.
la source
Cela peut être utilisé si vous souhaitez tester le résultat de la
doInBackground
méthode. Remplacez laonPostExecute
méthode et effectuez les tests là-bas. Pour attendre la fin de la tâche AsyncTask, utilisez CountDownLatch. Leslatch.await()
temps d' attente jusqu'à ce que le compte à rebours se déroule du 1 (qui est définie lors de l' initialisation) à 0 ( ce qui est fait par lacountdown()
méthode).la source
La plupart de ces solutions nécessitent beaucoup de code à écrire pour chaque test ou pour modifier la structure de votre classe. Ce que je trouve très difficile à utiliser si vous avez de nombreuses situations en cours de test ou de nombreuses AsyncTasks sur votre projet.
Il existe une bibliothèque qui facilite le processus de test
AsyncTask
. Exemple:Fondamentalement, il exécute votre AsyncTask et teste le résultat qu'il renvoie après l'
postComplete()
appel de.la source