Eu tenho um exemplo de implementação de AsyncTask muito simples e estou tendo problemas em testá-lo usando a estrutura Android JUnit.
Ele funciona muito bem quando eu instancio e executo em um aplicativo normal. No entanto, quando é executado a partir de qualquer uma das classes de estrutura do Android Testing (ou seja , AndroidTestCase , ActivityUnitTestCase , ActivityInstrumentationTestCase2 etc), ele se comporta de maneira estranha:
- Executa o
doInBackground()
método corretamente - No entanto, não invoca qualquer de seus métodos de notificação (
onPostExecute()
,onProgressUpdate()
, etc) - apenas os ignora sem aviso, sem mostrar quaisquer erros.
Este é um exemplo de AsyncTask muito simples:
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 é um caso de teste. Aqui, AsyncTaskDemoActivity é uma Activity muito simples que fornece IU para testar AsyncTask no 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 esse código está funcionando bem, exceto o fato de que AsyncTask não invoca seus métodos de notificação quando executado dentro do Android Testing Framework. Alguma ideia?
Service.doSomething()
?task.execute(Param...)
antesawait()
e colocarcountDown()
emonPostExecute(Result)
? (consulte stackoverflow.com/a/5722193/253468 ) Também @PeterAjtai,Service.doSomething
é uma chamada assíncrona comotask.execute
.Service.doSomething()
é onde você deve substituir sua chamada de serviço / tarefa assíncrona. Certifique-se de chamarsignal.countDown()
qualquer método que você precise implementar ou seu teste travará.Encontrei muitas respostas aproximadas, mas nenhuma delas juntou todas as partes corretamente. Portanto, esta é uma implementação correta ao usar um android.os.AsyncTask em seus casos de testes JUnit.
fonte
assertTrue(signal.await(...));
A maneira de lidar com isso é executar qualquer código que invoque uma AsyncTask em
runTestOnUiThread()
:Por padrão, o junit executa testes em um encadeamento separado da IU do aplicativo principal. A documentação do AsyncTask diz que a instância da tarefa e a chamada para execute () devem estar no thread de IU principal; isso ocorre porque AsyncTask depende do thread principal
Looper
eMessageQueue
de seu manipulador interno para funcionar corretamente.NOTA:
Eu recomendei anteriormente usar
@UiThreadTest
como decorador no método de teste para forçar o teste a ser executado no thread principal, mas isso não é muito adequado para testar uma AsyncTask porque enquanto seu método de teste está em execução no thread principal, nenhuma mensagem é processada no main MessageQueue - incluindo as mensagens que o AsyncTask envia sobre seu progresso, fazendo com que seu teste trave.fonte
runTestOnUiThread()
um método de teste com o@UiThreadTest
decorador? Isso não vai funcionar. Se um método de teste não tiver@UiThreadTest
, ele deve ser executado em seu próprio thread não principal por padrão.Deprecated in API level 24
developer.android.com/reference/android/test/…InstrumentationRegistry.getInstrumentation().runOnMainSync()
!Se você não se importa em executar o AsyncTask no thread do chamador (deve funcionar no caso de teste de unidade), você pode usar um Executor no thread atual, conforme descrito em https://stackoverflow.com/a/6583868/1266123
E então você executa seu AsyncTask em seu teste de unidade assim
Isso está funcionando apenas para HoneyComb e superior.
fonte
Eu escrevi unitests suficientes para Android e só quero compartilhar como fazer isso.
Em primeiro lugar, aqui está a classe auxiliar responsável por esperar e liberar o garçom. Nada especial:
SyncronizeTalker
A seguir, vamos criar uma interface com um método que deve ser chamado
AsyncTask
quando o trabalho for concluído. Claro, também queremos testar nossos resultados:TestTaskItf
Em seguida, vamos criar um esqueleto de nossa tarefa que iremos testar:
Enfim - nossa classe unitária:
TestBuildGroupTask
Isso é tudo.
Espero que ajude alguém.
fonte
Isso pode ser usado se você quiser testar o resultado do
doInBackground
método. Substitua oonPostExecute
método e execute os testes lá. Para aguardar a conclusão do AsyncTask, use CountDownLatch. Aslatch.await()
esperas até que a contagem regressiva vá de 1 (que é definido durante a inicialização) a 0 (que é feito pelocountdown()
método).fonte
A maioria dessas soluções requer que muitos códigos sejam escritos para cada teste ou para alterar a estrutura de sua classe. Que eu acho muito difícil de usar se você tiver muitas situações em teste ou muitas AsyncTasks em seu projeto.
Existe uma biblioteca que facilita o processo de teste
AsyncTask
. Exemplo:Basicamente, ele executa sua AsyncTask e testa o resultado que retorna após
postComplete()
ter sido chamado.fonte