Saya memiliki contoh implementasi AsyncTask yang sangat sederhana dan saya mengalami masalah dalam mengujinya menggunakan kerangka kerja Android JUnit.
Ini berfungsi dengan baik ketika saya memberi contoh dan menjalankannya dalam aplikasi normal. Namun ketika dijalankan dari salah satu kelas framework Pengujian Android (yaitu AndroidTestCase , ActivityUnitTestCase , ActivityInstrumentationTestCase2 , dll.) Ia berperilaku aneh:
- Ini menjalankan
doInBackground()
metode dengan benar - Namun tidak memanggil salah metode pemberitahuan nya (
onPostExecute()
,onProgressUpdate()
, dll) - hanya diam-diam mengabaikan mereka tanpa menunjukkan kesalahan.
Ini adalah contoh AsyncTask yang sangat sederhana:
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");
}
}
Ini adalah kasus uji. Di sini AsyncTaskDemoActivity adalah Aktivitas yang sangat sederhana yang menyediakan UI untuk menguji AsyncTask dalam 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());
}
}
Semua kode ini berfungsi dengan baik, kecuali fakta bahwa AsyncTask tidak memanggil metode pemberitahuannya ketika dijalankan oleh dalam Android Testing Framework. Ada ide?
Service.doSomething()
?task.execute(Param...)
sebelumawait()
dan menempatkancountDown()
dionPostExecute(Result)
? (lihat stackoverflow.com/a/5722193/253468 ) Juga @PeterAjtai,Service.doSomething
adalah panggilan asinkrontask.execute
.Service.doSomething()
adalah tempat Anda harus mengganti panggilan tugas layanan / asinkron Anda. Pastikan untuk memanggilsignal.countDown()
metode apa pun yang perlu Anda terapkan atau pengujian Anda akan macet.Saya menemukan banyak jawaban yang dekat tetapi tidak satupun dari mereka menyatukan semua bagian dengan benar. Jadi ini adalah salah satu implementasi yang benar saat menggunakan android.os.AsyncTask dalam kasus pengujian JUnit Anda.
sumber
assertTrue(signal.await(...));
Cara untuk mengatasinya adalah dengan menjalankan kode apa pun yang memanggil AsyncTask di
runTestOnUiThread()
:Secara default, junit menjalankan pengujian di thread terpisah dari UI aplikasi utama. Dokumentasi AsyncTask mengatakan bahwa instance tugas dan panggilan untuk mengeksekusi () harus ada di thread UI utama; ini karena AsyncTask bergantung pada utas utama
Looper
danMessageQueue
penangan internalnya agar berfungsi dengan benar.CATATAN:
Saya sebelumnya merekomendasikan penggunaan
@UiThreadTest
sebagai dekorator pada metode pengujian untuk memaksa pengujian berjalan di utas utama, tetapi ini kurang tepat untuk menguji AsyncTask karena sementara metode pengujian Anda berjalan di utas utama, tidak ada pesan yang diproses di utama MessageQueue - termasuk pesan yang dikirim AsyncTask tentang kemajuannya, menyebabkan pengujian Anda macet.sumber
runTestOnUiThread()
metode uji dengan@UiThreadTest
dekorator? Itu tidak akan berhasil. Jika metode pengujian tidak memilikinya@UiThreadTest
, ia harus berjalan di thread non-utamanya sendiri secara default.Deprecated in API level 24
developer.android.com/reference/android/test/…InstrumentationRegistry.getInstrumentation().runOnMainSync()
saja!Jika Anda tidak keberatan menjalankan AsyncTask di thread pemanggil (seharusnya baik-baik saja jika dilakukan pengujian Unit), Anda dapat menggunakan Executor di thread saat ini seperti yang dijelaskan di https://stackoverflow.com/a/6583868/1266123
Dan kemudian Anda menjalankan AsyncTask Anda dalam pengujian unit Anda seperti ini
Ini hanya berfungsi untuk HoneyComb dan yang lebih tinggi.
sumber
Saya menulis cukup unitests untuk Android dan hanya ingin berbagi cara melakukannya.
Pertama, inilah kelas helper yang bertanggung jawab untuk menunggu dan melepaskan waiter. Tidak ada yang spesial:
SyncronizeTalker
Selanjutnya, mari buat antarmuka dengan satu metode yang harus dipanggil dari
AsyncTask
saat pekerjaan selesai. Tentu kami juga ingin menguji hasil kami:TestTaskItf
Selanjutnya mari kita buat beberapa kerangka Tugas kita yang akan kita uji:
Akhirnya - kelas unitest kami:
TestBuildGroupTask
Itu saja.
Semoga bisa membantu seseorang.
sumber
Ini dapat digunakan jika Anda ingin menguji hasil dari
doInBackground
metode tersebut. GantionPostExecute
metode dan lakukan pengujian di sana. Untuk menunggu AsyncTask selesai, gunakan CountDownLatch. Thelatch.await()
menunggu sd berjalan mundur dari 1 (yang diatur selama inisialisasi) ke 0 (yang dilakukan olehcountdown()
metode).sumber
Sebagian besar solusi tersebut memerlukan banyak kode yang harus ditulis untuk setiap pengujian atau untuk mengubah struktur kelas Anda. Yang menurut saya sangat sulit digunakan jika Anda memiliki banyak situasi yang sedang diuji atau banyak AsyncTasks pada proyek Anda.
Ada perpustakaan yang memudahkan proses pengujian
AsyncTask
. Contoh:Pada dasarnya, ini menjalankan AsyncTask Anda dan menguji hasil yang dikembalikannya setelah
postComplete()
dipanggil.sumber