Tujuan
Pada Codelab kali ini Anda akan mempelajari bagaimana menguji ViewModel dalam proyek Academy dengan Unit Test dan Instrumental Test.
Logika Dasar
Unit Testing: Melakukan pengujian tiap ViewModel
Instrumental Testing: Melakukan pengujian tiap halaman dan keseluruhan Aplikasi.
Instrumental Testing: Melakukan pengujian tiap halaman dan keseluruhan Aplikasi.
Codelab Unit Testing ViewModel
- Bukalah proyek Academy yang sudah Anda buat pada modul sebelumnya atau Anda bisa unduh di sini:
- Pertama yang perlu dilakukan adalah menuliskan skenario testing yang dilakukan:
- AcademyViewModelTest:
- Memuat Courses:
- Memastikan data course tidak null.
- Memastikan jumlah data course sesuai dengan yang diharapkan.
- BookmarkViewModelTest:
- Memuat Bookmarks:
- Memastikan data course tidak null.
- Memastikan jumlah data course sesuai dengan yang diharapkan.
- DetailCourseViewModelTest:
- Memuat Course:
- Memastikan data course tidak null.
- Memastikan data course sesuai dengan yang diharapkan.
- Memuat Modules:
- Memastikan data module tidak null.
- Memastikan jumlah data module sesuai dengan yang diharapkan.
- CourseReaderViewModelTest:
- Memuat Modules:
- Memastikan data module tidak null.
- Memastikan jumlah data module sesuai dengan yang diharapkan.
- Memuat Module yang dipilih:
- Memastikan data module tidak null.
- Memastikan data content tidak null.
- Memastikan value dari content tidak null.
- Memastikan data content sesuai dengan yang diharapkan.
- Memuat Modules:
- Bukalah AcademyViewModel, kemudian klik ALT + Enter di bagian AcademyViewModel seperti ini:
Kemudian pilih Create Test, maka akan muncul tampilan seperti ini:Kotlin Java
Biarkan nama default seperti tampilan di atas, kemudian tekan tombol OK maka akan muncul pilihan tujuan pembuatan kelas. Karena Anda akan melakukan Unit Testing, maka pilih folder test.
Tekan tombol OK maka secara otomatis akan ada sebuah kelas baru dengan nama AcademyViewModelTesting.
Catatan:Hapuslah ExampleUnitTest, karena kita tidak akan memakai kelas tersebut. - Selanjutnya, bukalah kelas AcademyViewModelTest. Ujilah metode getCourses() dengan memasukkan kode berikut:
Kotlin class AcademyViewModelTest {
private lateinit var viewModel: AcademyViewModel
@Before
fun setUp() {
viewModel = AcademyViewModel()
}
@Test
fun getCourses() {
val courseEntities = viewModel.getCourses()
assertNotNull(courseEntities)
assertEquals(5, courseEntities.size)
}
}Java public class AcademyViewModelTest {
private AcademyViewModel viewModel;
@Before
public void setUp() {
viewModel = new AcademyViewModel();
}
@Test
public void getCourses() {
List<CourseEntity> courseEntities = viewModel.getCourses();
assertNotNull(courseEntities);
assertEquals(5, courseEntities.size());
}
}
- Jalankan pengujian pada kelas AcademyViewModelTest, dengan cara menekan tombol di sebelah kiri kode editor.
Pilihlah Run ‘AcademyViewModelTest’, maka hasil pengujiannya jadi seperti ini:
Selamat, Anda berhasil menguji AcademyViewModel dengan cara membandingkan ukuran dari array viewModel.getCourses(). - Lakukanlah kembali langkah nomor 2 untuk melakukan Unit Testing kelas BookmarkViewModel, DetailCoursesViewModel dan CourseReaderViewModel. Jika Anda sudah melakukan langkah tersebut, maka tampilan kelas pada package test akan jadi seperti ini:
- Bukalah BookmarkViewModelTest, kita akan menguji apakah metode getBookmark sudah benar atau belum. Tambahkan kode berikut:
Jika dilihat dari kode di atas, cara pengujian yang dilakukan sama karena data bookmark sama-sama berasal dari kelas DataDummy. Jalankan pengujian tersebut, maka akan jadi seperti ini:Kotlin class BookmarkViewModelTest {
private lateinit var viewModel: BookmarkViewModel
@Before
fun setUp() {
viewModel = BookmarkViewModel()
}
@Test
fun getBookmark() {
val courseEntities = viewModel.getBookmarks()
assertNotNull(courseEntities)
assertEquals(5, courseEntities.size)
}
}Java public class BookmarkViewModelTest {
private BookmarkViewModel viewModel;
@Before
public void setUp() {
viewModel = new BookmarkViewModel();
}
@Test
public void getBookmark() {
List<CourseEntity> courseEntities = viewModel.getBookmarks();
assertNotNull(courseEntities);
assertEquals(5, courseEntities.size());
}
}
Sekali lagi, selamat! Anda berhasil melakukan pengujian BookmarkViewModel dengan unit testing. Tapi masih ada 2 kelas ViewModel lagi yang perlu diuji. - Bukalah kelas DetailCourseViewModelTest. Pada kelas ini, terdapat 2 metode sumber data yakni getCourse() dan getModules(). Tambahkan kode berikut untuk melakukan pengujian terhadap 2 metode tersebut:
Jalankan pengujian pada kelas tersebut, maka hasil pengujiannya jadi seperti ini:Kotlin class DetailCourseViewModelTest {
private lateinit var viewModel: DetailCourseViewModel
private val dummyCourse = DataDummy.generateDummyCourses()[0]
private val courseId = dummyCourse.courseId
@Before
fun setUp() {
viewModel = DetailCourseViewModel()
viewModel.setSelectedCourse(courseId)
}
@Test
fun getCourse() {
viewModel.setSelectedCourse(dummyCourse.courseId)
val courseEntity = viewModel.getCourse()
assertNotNull(courseEntity)
assertEquals(dummyCourse.courseId, courseEntity.courseId)
assertEquals(dummyCourse.deadline, courseEntity.deadline)
assertEquals(dummyCourse.description, courseEntity.description)
assertEquals(dummyCourse.imagePath, courseEntity.imagePath)
assertEquals(dummyCourse.title, courseEntity.title)
}
@Test
fun getModules() {
val moduleEntities = viewModel.getModules()
assertNotNull(moduleEntities)
assertEquals(7, moduleEntities.size.toLong())
}
}Java public class DetailCourseViewModelTest {
private DetailCourseViewModel viewModel;
private CourseEntity dummyCourse = DataDummy.generateDummyCourses().get(0);
private String courseId = dummyCourse.getCourseId();
@Before
public void setUp() {
viewModel = new DetailCourseViewModel();
viewModel.setSelectedCourse(courseId);
}
@Test
public void getCourse() {
viewModel.setSelectedCourse(dummyCourse.getCourseId());
CourseEntity courseEntity = viewModel.getCourse();
assertNotNull(courseEntity);
assertEquals(dummyCourse.getCourseId(), courseEntity.getCourseId());
assertEquals(dummyCourse.getDeadline(), courseEntity.getDeadline());
assertEquals(dummyCourse.getDescription(), courseEntity.getDescription());
assertEquals(dummyCourse.getImagePath(), courseEntity.getImagePath());
assertEquals(dummyCourse.getTitle(), courseEntity.getTitle());
}
@Test
public void getModules() {
List<ModuleEntity> moduleEntities = viewModel.getModules();
assertNotNull(moduleEntities);
assertEquals(7, moduleEntities.size());
}
}
Selamat, Anda juga berhasil menguji DetailCourseViewModel dengan cara membandingkan ukuran dari array viewModel.getModules() dan membandingkan data CourseEntity hasil dari viewModel.getCourses().
- Terakhir, bukalah CourseReaderViewModelTest. Pada kelas CourseReaderViewModel, terdapat 2 metode yang harus diuji yakni getSelectedModule dan getModules. Tambahkan kode berikut untuk melakukan pengujian:
Kotlin class CourseReaderViewModelTest {
private lateinit var viewModel: CourseReaderViewModel
private val dummyCourse = DataDummy.generateDummyCourses()[0]
private val courseId = dummyCourse.courseId
private val dummyModules = DataDummy.generateDummyModules(courseId)
private val moduleId = dummyModules[0].moduleId
@Before
fun setUp() {
viewModel = CourseReaderViewModel()
viewModel.setSelectedCourse(courseId)
viewModel.setSelectedModule(moduleId)
val dummyModule = dummyModules[0]
dummyModule.contentEntity = ContentEntity("<h3 class=\\\"fr-text-bordered\\\">"+dummyModule.title+"</h3><p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>")
}
@Test
fun getModules() {
val moduleEntities = viewModel.getModules()
assertNotNull(moduleEntities)
assertEquals(7, moduleEntities.size.toLong())
}
@Test
fun getSelectedModule() {
val moduleEntity = viewModel.getSelectedModule()
assertNotNull(moduleEntity)
val contentEntity = moduleEntity.contentEntity
assertNotNull(contentEntity)
val content = contentEntity?.content
assertNotNull(content)
assertEquals(content, dummyModules[0].contentEntity?.content)
}
}Java public class CourseReaderViewModelTest {
private CourseReaderViewModel viewModel;
private CourseEntity dummyCourse = DataDummy.generateDummyCourses().get(0);
private String courseId = dummyCourse.getCourseId();
private ArrayList<ModuleEntity> dummyModules = DataDummy.generateDummyModules(courseId);
private String moduleId = dummyModules.get(0).getModuleId();
@Before
public void setUp() {
viewModel = new CourseReaderViewModel();
viewModel.setSelectedCourse(courseId);
viewModel.setSelectedModule(moduleId);
ModuleEntity dummyModule = dummyModules.get(0);
dummyModule.contentEntity = new ContentEntity("<h3 class=\\\"fr-text-bordered\\\">" + dummyModule.getTitle() + "</h3><p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>");
}
@Test
public void getModules() {
ArrayList<ModuleEntity> moduleEntities = viewModel.getModules();
assertNotNull(moduleEntities);
assertEquals(7, moduleEntities.size());
}
@Test
public void getSelectedModule() {
ModuleEntity moduleEntity = viewModel.getSelectedModule();
assertNotNull(moduleEntity);
ContentEntity contentEntity = moduleEntity.contentEntity;
assertNotNull(contentEntity);
String content = contentEntity.getContent();
assertNotNull(content);
assertEquals(content, dummyModules.get(0).contentEntity.getContent());
}
}Jalankan pengujian pada kelas tersebut, maka hasil pengujiannya jadi seperti ini:
Selamat, Anda juga berhasil menguji CourseReaderViewModel dengan cara membandingkan ukuran dari array viewModel.getModules() dan membandingkan data ModuleEntity hasil dari viewModel.getSelectedModule().
Mantap! Anda sudah melakukan pengujian setiap ViewModel yang ada pada proyek Academy. Namun, Anda juga perlu melakukan Instrumental Test untuk menguji apakah secara ui, kode Anda sudah berjalan sudah benar.
Codelab Instrumental Testing ViewModel
- Sebelum Anda melakukan Instrumental Testing, Anda perlu membuat skenario testing yang dilakukan:
- Menampilkan data kursus academy
- Memastikan rv_academy dalam keadaan tampil.
- Gulir rv_academy ke posisi data terakhir.
- Menampilkan data detail kursus academy
- Memberi tindakan klik pada data pertama di rv_academy.
- Memastikan TextView untuk title tampil sesuai dengan yang diharapkan.
- Memastikan TextView untuk deadline tampil sesuai dengan yang diharapkan.
- Menampilkan data modul
- Memberi tindakan klik pada data pertama di rv_academy.
- Memberi tindakan klik pada btn_start.
- Memastikan rv_module dalam keadaan tampil.
- Menampilkan data kursus academy
- Memberi tindakan klik pada data pertama di rv_academy.
- Memberi tindakan klik pada btn_start.
- Memberi tindakan klik pada data pertama di rv_module
- Memastikan web_view sudah tampil.
- Menampilkan data bookmark
- Klik TabLayout dengan teks Bookmark
- Memastikan rv_module dalam keadaan tampil.
- Gulir rv_module ke posisi data terakhir.
- Menampilkan data kursus academy
- Selanjutnya Anda perlu menambahkan library yang digunakan untuk pengujian. Bukalah build.gradle (project), tambahkan kode berikut:
ext {
//dependencies version
appCompatVersion = '1.1.0'
coreVersion = '1.1.0'
constraintLayoutVersion = '1.1.3'
junitVersion = '4.12'
espressoVersion = '3.1.0'
androidXTestVersion = '1.2.0'
materialVersion = '1.0.0'
recyclerViewVersion = '1.1.0'
glideVersion = '4.10.0'
archLifecycleVersion = '2.1.0'
}Kemudian bukalah build.gradle (module:app), tambahkan kode berikut:androidTestImplementation "androidx.test.espresso:espresso-contrib:$espressoVersion"
androidTestImplementation "androidx.test:rules:$androidXTestVersion" - Bukalah HomeActivity, kemudian klik ALT + Enter di bagian HomeActivity seperti ini:
Kemudian pilih Create Test, maka akan muncul tampilan seperti ini:Kotlin Java
Biarkan nama default seperti tampilan di atas, kemudian tekan tombol OK maka akan muncul pilihan tujuan pembuatan kelas. Karena Anda akan melakukan Instrumental Testing, maka pilih folder androidTest.
Tekan tombol OK, secara otomatis akan ada sebuah kelas baru dengan nama HomeActivityTest.
Catatan:Hapuslah ExampleInstrumentedTest, karena kita tidak akan memakai kelas tersebut. - Bukalah kelas HomeAcademyTest, buatlah pengujian sesuai dengan skenario seperti berikut:
Kotlin class HomeActivityTest {
private val dummyCourse = DataDummy.generateDummyCourses()
@get:Rule
var activityRule = ActivityTestRule(HomeActivity::class.java)
@Test
fun loadCourses() {
onView(withId(R.id.rv_academy)).check(matches(isDisplayed()))
onView(withId(R.id.rv_academy)).perform(RecyclerViewActions.scrollToPosition<RecyclerView.ViewHolder>(dummyCourse.size))
}
@Test
fun loadDetailCourse() {
onView(withId(R.id.rv_academy)).perform(RecyclerViewActions.actionOnItemAtPosition<RecyclerView.ViewHolder>(0, click()))
onView(withId(R.id.text_title)).check(matches(isDisplayed()))
onView(withId(R.id.text_title)).check(matches(withText(dummyCourse[0].title)))
onView(withId(R.id.text_date)).check(matches(isDisplayed()))
onView(withId(R.id.text_date)).check(matches(withText("Deadline ${dummyCourse[0].deadline}")))
}
@Test
fun loadModule() {
onView(withId(R.id.rv_academy)).perform(RecyclerViewActions.actionOnItemAtPosition<RecyclerView.ViewHolder>(0, click()))
onView(withId(R.id.btn_start)).perform(click())
onView(withId(R.id.rv_module)).check(matches(isDisplayed()))
}
@Test
fun loadDetailModule() {
onView(withId(R.id.rv_academy)).perform(RecyclerViewActions.actionOnItemAtPosition<RecyclerView.ViewHolder>(0, click()))
onView(withId(R.id.btn_start)).perform(click())
onView(withId(R.id.rv_module)).perform(RecyclerViewActions.actionOnItemAtPosition<RecyclerView.ViewHolder>(0, click()))
onView(withId(R.id.web_view)).check(matches(isDisplayed()))
}
@Test
fun loadBookmarks() {
onView(withText("Bookmark")).perform(click())
onView(withId(R.id.rv_bookmark)).check(matches(isDisplayed()))
onView(withId(R.id.rv_bookmark)).perform(RecyclerViewActions.scrollToPosition<RecyclerView.ViewHolder>(dummyCourse.size))
}
}Java public class HomeActivityTest {
private ArrayList<CourseEntity> dummyCourse = DataDummy.generateDummyCourses();
@Rule
public ActivityTestRule activityRule = new ActivityTestRule<>(HomeActivity.class);
@Test
public void loadCourses() {
onView(withId(R.id.rv_academy)).check(matches(isDisplayed()));
onView(withId(R.id.rv_academy)).perform(RecyclerViewActions.scrollToPosition(dummyCourse.size()));
}
@Test
public void loadDetailCourse() {
onView(withId(R.id.rv_academy)).perform(RecyclerViewActions.actionOnItemAtPosition(0, click()));
onView(withId(R.id.text_title)).check(matches(isDisplayed()));
onView(withId(R.id.text_title)).check(matches(withText(dummyCourse.get(0).getTitle())));
onView(withId(R.id.text_date)).check(matches(isDisplayed()));
onView(withId(R.id.text_date)).check(matches(withText(String.format("Deadline %s", dummyCourse.get(0).getDeadline()))));
}
@Test
public void loadModule() {
onView(withId(R.id.rv_academy)).perform(RecyclerViewActions.actionOnItemAtPosition(0, click()));
onView(withId(R.id.btn_start)).perform(click());
onView(withId(R.id.rv_module)).check(matches(isDisplayed()));
}
@Test
public void loadDetailModule() {
onView(withId(R.id.rv_academy)).perform(RecyclerViewActions.actionOnItemAtPosition(0, click()));
onView(withId(R.id.btn_start)).perform(click());
onView(withId(R.id.rv_module)).perform(RecyclerViewActions.actionOnItemAtPosition(0, click()));
onView(withId(R.id.web_view)).check(matches(isDisplayed()));
}
@Test
public void loadBookmarks() {
onView(withText("Bookmark")).perform(click());
onView(withId(R.id.rv_bookmark)).check(matches(isDisplayed()));
onView(withId(R.id.rv_bookmark)).perform(RecyclerViewActions.scrollToPosition(dummyCourse.size()));
}
} - Kemudian jalankan test dengan cara klik kanan pada class HomeActivityTest dan klik Run 'HomeActivityTest'.
Bisa juga dengan klik icon hijau seperti gambar ini:
Maka device akan otomatis melakukan aksi sesuai dengan yang diskenariokan seperti ini:
Dan kalau dilihat hasil testnya seperti ini:Selamat! Anda berhasil menguji AcademyTest.
Bedah Kode
UnitTest
Materi unit test secara umum sama dengan yang ada di modul latihan. Anda bisa lihat kembali untuk fungsi test yang digunakan seperti assert, verify, when() dll.
Tiga Komponen Utama dari Espresso:
Anda menggunakan Espresso dalam melakukan Instrumental Testing, berikut adalah 3 komponen utamanya:
- ViewMatchers (onView(ViewMatcher)): untuk menemukan elemen atau komponen antarmuka yang diuji.
- ViewActions (perform(ViewAction)): untuk memberikan event untuk melakukan sebuah aksi pada komponen antarmuka yang diuji.
- ViewAssertions: sebuah kondisi atau state dari komponen yang diuji.
Perhatikan kode berikut:
Kotlin |
@get:Rule |
Java |
@Rule |
Annotation Rule digunakan untuk memanggil activity mana yang akan di-launch atau diluncurkan.
Selain itu perhartikan kode berikut:
Selain itu perhartikan kode berikut:
Kotlin |
onView(withId(R.id.rv_academy)).perform(RecyclerViewActions.scrollToPosition<RecyclerView.ViewHolder>(dummyCourse.size)) |
Java |
onView(withId(R.id.rv_academy)).perform(RecyclerViewActions.scrollToPosition(dummyCourse.size())); |
Jika diperhartikan, di sini terdapat kode RecyclerViewActions yang digunakan untuk pengujian pada RecyclerView seperti scroll ke posisi tertentu dan klik pada posisi tertentu. Kode ini merupakan hasil dari penambahan library
androidTestImplementation "androidx.test.espresso:espresso-contrib:$espressoVersion"
Menarik bukan? Jadi ketika Anda membuat sebuah fitur, Anda perlu menyiapkan skenario pengujian untuk menguji fitur tersebut. Karena jika tidak, Aplikasi yang Anda buat bisa saja menyimpan eror atau bug yang tidak Anda ketahui.
Anda bisa unduh proyek Academy tentang Pengujian ViewModel di sini:
Codelabs selanjutnya akan membahas tentang Repository yang ada pada proyek Academy. Tetap semangat!