API pengujian Compose versi v2 (createComposeRule,
createAndroidComposeRule, runComposeUiTest,
runAndroidComposeUiTest, dll.) kini tersedia untuk meningkatkan kontrol atas
eksekusi coroutine. Update ini tidak menduplikasi seluruh platform API;
hanya API yang membuat lingkungan pengujian yang telah diupdate.
API v1 tidak digunakan lagi, dan sangat direkomendasikan untuk bermigrasi ke API baru. Migrasi memverifikasi bahwa pengujian Anda selaras dengan perilaku coroutine standar dan menghindari masalah kompatibilitas di masa mendatang. Untuk mengetahui daftar API v1 yang tidak digunakan lagi, lihat Pemetaan API.
Meskipun API v1 mengandalkan UnconfinedTestDispatcher, API v2 menggunakan
StandardTestDispatcher secara default untuk komposisi yang berjalan. Perubahan ini
menyelaraskan perilaku pengujian Compose dengan runTest API standar dan memberikan
kontrol eksplisit atas urutan eksekusi coroutine.
Mengonfigurasi lingkungan pengujian
API Compose test v2 menggunakan ComposeUiTestConfig untuk menyesuaikan lingkungan
pengujian. API yang membuat fungsi penyiapan untuk pengujian, seperti
createComposeRule, runComposeUiTest, dan API terkait lainnya,
menerima ComposeUiTestConfig. Objek konfigurasi ini menggabungkan
API terkait lingkungan seperti effectContext, runTestContext,
dan testTimeout menjadi satu objek.
Model konfigurasi juga mengelola inputMode. API Compose test v2
menerapkan InputMode.Touch secara default di awal setiap pengujian untuk memastikan
determinisme dan mencegah status mode input bocor di antara pengujian.
ComposeUiTestConfig adalah bagian dari API pengujian Compose v2, yang menggunakan
StandardTestDispatcher secara default. Jika pengujian Anda menggunakan API v1,
lihat Bermigrasi ke API pengujian v2 sebelum mengadopsi
ComposeUiTestConfig.
Bermigrasi ke ComposeUiTestConfig
Dalam overload untuk membuat fungsi penyiapan dalam pengujian, beberapa overload
yang menerima parameter konfigurasi individual -- seperti effectContext,
runTestContext, atau testTimeout -- tidak digunakan lagi. Update pengujian Anda untuk menggunakan
ComposeUiTestConfig, seperti yang ditunjukkan dalam contoh berikut:
val testConfig = ComposeUiTestConfig( effectContext = EmptyCoroutineContext, runTestContext = EmptyCoroutineContext, testTimeout = 30.seconds ) @get:Rule val rule = createComposeRule(config = testConfig) // OR runComposeUiTest(config = testConfig) {}
Mode input default
Pengujian dapat gagal selama migrasi jika mengandalkan mode input non-sentuh
yang dikonfigurasi melalui API instrumentasi sebelum pengujian dimulai. Dalam fungsi penyiapan untuk pengujian, sistem menerapkan InputMode.Touch secara default di awal setiap pengujian untuk determinisme yang lebih baik dan untuk mencegah kebocoran status, menggantikan status perangkat sekitar dan penyiapan pra-pengujian.
Untuk mengatasinya, tentukan mode input yang diperlukan di ComposeUiTestConfig:
class FocusTest { @get:Rule val rule = createComposeRule( config = ComposeUiTestConfig(inputMode = InputMode.Keyboard) ) @Test fun testFocus() {} }
Untuk mengonfigurasi mode input untuk setiap kasus pengujian, bukan seluruh
class pengujian, teruskan ComposeUiTestConfig ke runComposeUiTest:
class FocusTest { @Test fun testTouchMode() = runComposeUiTest { // Runs with the default InputMode.Touch } @Test fun testKeyboardMode() = runComposeUiTest( ComposeUiTestConfig(inputMode = InputMode.Keyboard) ) { // Runs with InputMode.Keyboard } }
Untuk masalah dan penyelesaian migrasi lainnya, lihat Kegagalan umum dan cara memperbaikinya.
Bermigrasi ke API pengujian v2
Saat mengupgrade ke API v2, Anda biasanya dapat menggunakan Temukan + Ganti untuk mengupdate impor paket dan menerapkan perubahan dispatcher baru.
Atau, minta Gemini untuk melakukan migrasi ke v2 API pengujian Compose dengan perintah berikut:
Perintah AI
Bermigrasi dari API pengujian v1 ke API pengujian v2
Perintah ini akan menggunakan panduan ini untuk bermigrasi ke API pengujian v2.
Migrate to Compose testing v2 APIs using the official
migration guide.Gunakan tabel berikut untuk memetakan API v1 yang tidak digunakan lagi ke pengganti v2-nya:
Tidak digunakan lagi (v1) |
Penggantian (v2) |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Kompatibilitas mundur dan pengecualian
API v1 yang ada kini tidak digunakan lagi, tetapi terus menggunakan
UnconfinedTestDispatcher untuk mempertahankan perilaku yang ada dan mencegah perubahan yang dapat menyebabkan gangguan.
Berikut adalah satu-satunya pengecualian saat perilaku default telah berubah:
Dispatcher pengujian default yang digunakan untuk menjalankan komposisi di
class AndroidComposeUiTestEnvironment telah beralih dari
UnconfinedTestDispatcher ke StandardTestDispatcher. Hal ini memengaruhi kasus saat
Anda membuat instance menggunakan konstruktor, atau subclass
AndroidComposeUiTestEnvironment, dan memanggil konstruktor tersebut.
Perubahan penting: Dampak pada eksekusi coroutine
Perbedaan utama antara API v1 dan v2 adalah cara coroutine dikirim:
- API v1 (
UnconfinedTestDispatcher): Saat coroutine diluncurkan, coroutine akan langsung dieksekusi di thread saat ini, sering kali selesai sebelum baris kode pengujian berikutnya dijalankan. Tidak seperti perilaku produksi, eksekusi langsung ini dapat secara tidak sengaja menyamarkan masalah pengaturan waktu yang sebenarnya atau kondisi race yang akan terjadi dalam aplikasi aktif. - API v2 (
StandardTestDispatcher): Saat coroutine diluncurkan, coroutine akan diantrekan dan tidak akan dieksekusi hingga pengujian secara eksplisit memajukan jam virtual. API pengujian Compose standar (sepertiwaitForIdle()) sudah menangani sinkronisasi ini, sehingga sebagian besar pengujian yang mengandalkan API standar ini akan terus berfungsi tanpa perubahan.
Kegagalan umum dan cara memperbaikinya
Jika pengujian Anda gagal setelah diupgrade ke v2, kemungkinan pengujian tersebut menunjukkan pola berikut:
- Kegagalan: Anda meluncurkan tugas (misalnya, ViewModel memuat data), tetapi pernyataan Anda langsung gagal karena data masih dalam status "Loading".
- Penyebab: Dengan API v2, coroutine dimasukkan ke dalam antrean, bukan langsung dieksekusi. Tugas dimasukkan ke dalam antrean, tetapi tidak pernah benar-benar dijalankan sebelum hasilnya diperiksa.
- Perbaikan: Majukan waktu secara eksplisit. Anda harus secara eksplisit memberi tahu dispatcher v2 kapan harus menjalankan pekerjaan.
Pendekatan sebelumnya
Di v1, tugas diluncurkan dan langsung selesai. Di v2, kode berikut
gagal karena loadData() belum benar-benar berjalan.
// In v1, this launched and finished immediately.
viewModel.loadData()
// In v2, this fails because loadData() hasn't actually run yet!
assertEquals(Success, viewModel.state.value)
Pendekatan yang direkomendasikan
Gunakan waitForIdle atau runOnIdle untuk menjalankan tugas dalam antrean sebelum
menegaskan.
Opsi 1: Menggunakan waitForIdle akan memajukan waktu hingga UI tidak ada aktivitas,
yang memverifikasi bahwa coroutine telah berjalan.
viewModel.loadData()
// Explicitly run all queued tasks
composeTestRule.waitForIdle()
assertEquals(Success, viewModel.state.value)
Opsi 2: Menggunakan runOnIdle akan mengeksekusi blok kode di UI thread setelah
UI menjadi tidak ada aktivitas.
viewModel.loadData()
// Run the assertion after the UI is idle
composeTestRule.runOnIdle {
assertEquals(Success, viewModel.state.value)
}
Sinkronisasi manual
Dalam skenario yang melibatkan sinkronisasi manual, seperti saat maju otomatis
dinonaktifkan, peluncuran coroutine tidak akan langsung dieksekusi karena
jam pengujian dijeda. Untuk menjalankan coroutine dalam antrean tanpa
memajukan jam virtual, gunakan runCurrent() API. Fungsi ini menjalankan tugas yang dijadwalkan untuk waktu virtual saat ini.
composeTestRule.mainClock.scheduler.runCurrent()
Berbeda dengan waitForIdle(), yang memajukan jam pengujian hingga UI stabil, runCurrent() menjalankan tugas tertunda sambil mempertahankan waktu virtual saat ini. Perilaku ini memungkinkan verifikasi status perantara yang
akan dilewati jika jam dimajukan ke status tidak ada aktivitas.
Penjadwal pengujian dasar yang digunakan di lingkungan pengujian akan diekspos. Penjadwal ini dapat digunakan bersama dengan API runTest Kotlin untuk menyinkronkan jam pengujian.
Bermigrasi ke runComposeUiTest
Jika Anda menggunakan API pengujian Compose bersama API runTest Kotlin,
sebaiknya beralih ke runComposeUiTest.
Pendekatan sebelumnya
Menggunakan createComposeRule bersama dengan runTest akan membuat dua clock
terpisah: satu untuk Compose, dan satu untuk cakupan coroutine pengujian. Konfigurasi ini dapat memaksa Anda untuk menyinkronkan penjadwal pengujian secara manual.
@get:Rule val composeTestRule = createComposeRule() @Test fun testWithCoroutines() { composeTestRule.setContent { var status by remember { mutableStateOf("Loading...") } LaunchedEffect(Unit) { delay(1000) status = "Done!" } Text(text = status) } // NOT RECOMMENDED // Fails: runTest creates a new, separate scheduler. // Advancing time here does NOT advance the compose clock. // To fix this without migrating, you would need to share the scheduler // by passing 'composeTestRule.mainClock.scheduler' to runTest. runTest { composeTestRule.onNodeWithText("Loading...").assertIsDisplayed() advanceTimeBy(1000) composeTestRule.onNodeWithText("Done!").assertIsDisplayed() } }
Pendekatan yang direkomendasikan
runComposeUiTest API otomatis menjalankan blok pengujian dalam cakupan
runTest-nya sendiri. Jam pengujian disinkronkan dengan lingkungan Compose, sehingga
Anda tidak perlu lagi mengelola penjadwal secara manual.
@Test fun testWithCoroutines() = runComposeUiTest { setContent { var status by remember { mutableStateOf("Loading...") } LaunchedEffect(Unit) { delay(1000) status = "Done!" } Text(text = status) } onNodeWithText("Loading...").assertIsDisplayed() mainClock.advanceTimeBy(1000 + 16 /* Frame buffer */) onNodeWithText("Done!").assertIsDisplayed() } }