Tengo un ejemplo de implementación de AsyncTask muy simple y tengo problemas para probarlo con el marco de Android JUnit.
Funciona bien cuando lo instancia y lo ejecuto en una aplicación normal. Sin embargo, cuando se ejecuta desde cualquiera de las clases del marco de pruebas de Android (es decir , AndroidTestCase , ActivityUnitTestCase , ActivityInstrumentationTestCase2 , etc.) se comporta de manera extraña:
- Ejecuta el
doInBackground()
método correctamente - Sin embargo, no invoca ninguna de sus métodos de notificación (
onPostExecute()
,onProgressUpdate()
, etc) - sólo los ignora en silencio sin mostrar ningún error.
Este es un ejemplo de AsyncTask muy 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");
}
}
Este es un caso de prueba. Aquí AsyncTaskDemoActivity es una actividad muy simple que proporciona una interfaz de usuario para probar AsyncTask en el modo:
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());
}
}
Todo este código funciona bien, excepto el hecho de que AsyncTask no invoca sus métodos de notificación cuando se ejecuta dentro de Android Testing Framework. ¿Algunas ideas?
Service.doSomething()
?task.execute(Param...)
antesawait()
y ponercountDown()
enonPostExecute(Result)
? (ver stackoverflow.com/a/5722193/253468 ) También @PeterAjtai,Service.doSomething
es una llamada asíncrona comotask.execute
.Service.doSomething()
es donde debe reemplazar su servicio / llamada de tarea asíncrona. Asegúrese de llamarsignal.countDown()
a cualquier método que necesite implementar o su prueba se bloqueará.Encontré muchas respuestas cercanas, pero ninguna reunió todas las partes correctamente. Entonces, esta es una implementación correcta cuando se usa android.os.AsyncTask en sus casos de prueba JUnit.
fuente
assertTrue(signal.await(...));
La forma de lidiar con esto es ejecutar cualquier código que invoque una AsyncTask en
runTestOnUiThread()
:De forma predeterminada, junit ejecuta las pruebas en un hilo distinto al de la interfaz de usuario de la aplicación principal. La documentación de AsyncTask dice que la instancia de la tarea y la llamada a execute () deben estar en el hilo principal de la IU; esto se debe a que AsyncTask depende del hilo principal
Looper
yMessageQueue
de que su controlador interno funcione correctamente.NOTA:
Anteriormente recomendé usar
@UiThreadTest
como decorador en el método de prueba para forzar que la prueba se ejecute en el hilo principal, pero esto no es del todo correcto para probar una AsyncTask porque mientras su método de prueba se está ejecutando en el hilo principal, no se procesan mensajes en el Main MessageQueue: incluidos los mensajes que AsyncTask envía sobre su progreso, lo que hace que la prueba se cuelgue.fuente
runTestOnUiThread()
un método de prueba con el@UiThreadTest
decorador? Eso no funcionará. Si un método de prueba no lo tiene@UiThreadTest
, debe ejecutarse en su propio subproceso no principal de forma predeterminada.Deprecated in API level 24
developer.android.com/reference/android/test/…InstrumentationRegistry.getInstrumentation().runOnMainSync()
en su lugar!Si no le importa ejecutar AsyncTask en el hilo de la persona que llama (debería estar bien en el caso de las pruebas unitarias), puede usar un Ejecutor en el hilo actual como se describe en https://stackoverflow.com/a/6583868/1266123
Y luego ejecuta su AsyncTask en su prueba unitaria de esta manera
Esto solo funciona para HoneyComb y versiones posteriores.
fuente
Escribí suficientes conjuntos para Android y solo quiero compartir cómo hacerlo.
En primer lugar, aquí está la clase de ayudante responsable de esperar y liberar al camarero. Nada especial:
SyncronizeTalker
A continuación, creemos una interfaz con un método que se debe llamar desde
AsyncTask
que se realiza el trabajo. Seguro que también queremos probar nuestros resultados:TestTaskItf
A continuación, creemos un esqueleto de nuestra tarea que probaremos:
Por fin, nuestra clase más unitaria:
TestBuildGroupTask
Eso es todo.
Espero que ayude a alguien.
fuente
Esto se puede utilizar si desea probar el resultado del
doInBackground
método. Anule elonPostExecute
método y realice las pruebas allí. Para esperar a que AsyncTask se complete, use CountDownLatch. Lalatch.await()
espera hasta que la cuenta regresiva va de 1 (que se establece durante la inicialización) a 0 (que se realiza mediante elcountdown()
método).fuente
La mayoría de esas soluciones requieren que se escriba mucho código para cada prueba o para cambiar la estructura de su clase. Lo cual encuentro muy difícil de usar si tienes muchas situaciones bajo prueba o muchas AsyncTasks en tu proyecto.
Hay una biblioteca que facilita el proceso de prueba
AsyncTask
. Ejemplo:Básicamente, ejecuta su AsyncTask y prueba el resultado que devuelve después de que
postComplete()
se haya llamado a.fuente