Pengujian unit, teknik pengujian yang menggunakan modul individual yang diuji untuk menentukan apakah ada masalah oleh pengembang sendiri. Ini berkaitan dengan kebenaran fungsional modul mandiri.

Jadi mengapa tes menulis begitu penting?
Pengujian kami sebenarnya melindungi implementasi, dan setiap perubahan di masa mendatang yang merusak kode juga akan merusak pengujian. Dengan kata lain, kami akan diberitahu jika ada perubahan yang melanggar kode.
Arsitektur yang baik memisahkan masalah menjadi komponen dan membuat pengujian Unit menjadi lebih mudah.
Dalam tutorial ini, saya akan menjelaskan Cara menulis unit test untuk ViewModel. Artikel ini akan mengimplementasikan MVVM dan LiveData.
Model-View-ViewModel

- View — yang menginformasikan ViewModel tentang tindakan pengguna
- ViewModel — mengekspos aliran data yang relevan dengan View
- Model adalah tempat logika bisnis berada. Itu juga tidak memiliki pengetahuan tentang ViewModel melainkan mengirimkan pemberitahuan yang dapat diamati saat diperbarui.
View dan ViewModel berkomunikasi melalui LiveData

Bagian penting di sini adalah View (Activity/Fragments), menyimpan semua logika di ViewModel, juga mengirimkan tindakan pengguna segera ke ViewModel menggunakan LiveData. Kita harus menghindari semua dependensi terkait Android di ViewModel agar dapat menguji dengan uji JUnit murni.
Mari masuk ke kode:
Saya telah menggunakan Dagger untuk menyediakan dependensi, RxJava untuk panggilan dan threading API, LiveData untuk memberi tahu tampilan.
Harap membiasakan diri Anda dengan dasar-dasar Pengujian JUnit
@Sebelum, @Setelah,@Uji, @Mock,@RunDengan, anotasi
https://www.vogella.com/tutorials/JUnit/article.html
Sebelum melanjutkan membaca, saya sangat menyarankan Anda untuk melihat kode sumber aplikasi untuk memahami cara kerjanya. Anda dapat menemukan seluruh tautan kode di Github (https://github.com/droiddevgeeks/NewsApp)
ViewModel class
public class NewsViewModel extends ViewModel {
private CompositeDisposable disposable;
private final NewsApiClient apiClient;
private final RxSingleSchedulers rxSingleSchedulers;
private final MutableLiveData<NewsListViewState> newsListState = new MutableLiveData<>();
public MutableLiveData<NewsListViewState> getNewsListState() {
return newsListState;
}
@Inject
public NewsViewModel(NewsApiClient apiClient, RxSingleSchedulers rxSingleSchedulers) {
this.apiClient = apiClient;
this.rxSingleSchedulers = rxSingleSchedulers;
disposable = new CompositeDisposable();
}
public void fetchNews() {
disposable.add(apiClient.fetchNews()
.doOnEvent((newsList, throwable) -> onLoading())
.compose(rxSingleSchedulers.applySchedulers())
.subscribe(this::onSuccess,
this::onError));
}
private void onSuccess(NewsList newsList) {
NewsListViewState.SUCCESS_STATE.setData(newsList);
newsListState.postValue(NewsListViewState.SUCCESS_STATE);
}
private void onError(Throwable error) {
NewsListViewState.ERROR_STATE.setError(error);
newsListState.postValue(NewsListViewState.ERROR_STATE);
}
private void onLoading() {
newsListState.postValue(NewsListViewState.LOADING_STATE);
}
@Override
protected void onCleared() {
super.onCleared();
if (disposable != null) {
disposable.clear();
disposable = null;
}
}
}
NewsViewState class
public class NewsListViewState extends BaseViewState<NewsList> {
private NewsListViewState(NewsList data, int currentState, Throwable error) {
this.data = data;
this.error = error;
this.currentState = currentState;
}
public static NewsListViewState ERROR_STATE = new NewsListViewState(null, State.FAILED.value, new Throwable());
public static NewsListViewState LOADING_STATE = new NewsListViewState(null, State.LOADING.value, null);
public static NewsListViewState SUCCESS_STATE = new NewsListViewState(new NewsList(), State.SUCCESS.value, null);
}
BaseView State
public class BaseViewState<T> {
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public Throwable getError() {
return error;
}
public void setError(Throwable error) {
this.error = error;
}
public int getCurrentState() {
return currentState;
}
public void setCurrentState(int currentState) {
this.currentState = currentState;
}
protected T data;
protected Throwable error;
protected int currentState;
public enum State{
LOADING(0), SUCCESS(1),FAILED(-1);
public int value;
State(int val) {
value = val;
}
}
}
ViewModelTest class
@RunWith(JUnit4.class)
public class NewsViewModelTest {
@Rule
public InstantTaskExecutorRule instantExecutorRule = new InstantTaskExecutorRule();
@Mock
ApiEndPoint apiEndPoint;
@Mock
NewsApiClient apiClient;
private NewsViewModel viewModel;
@Mock
Observer<NewsListViewState> observer;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
viewModel = new NewsViewModel(apiClient, RxSingleSchedulers.TEST_SCHEDULER);
viewModel.getNewsListState().observeForever(observer);
}
@Test
public void testNull() {
when(apiClient.fetchNews()).thenReturn(null);
assertNotNull(viewModel.getNewsListState());
assertTrue(viewModel.getNewsListState().hasObservers());
}
@Test
public void testApiFetchDataSuccess() {
// Mock API response
when(apiClient.fetchNews()).thenReturn(Single.just(new NewsList()));
viewModel.fetchNews();
verify(observer).onChanged(NewsListViewState.LOADING_STATE);
verify(observer).onChanged(NewsListViewState.SUCCESS_STATE);
}
@Test
public void testApiFetchDataError() {
when(apiClient.fetchNews()).thenReturn(Single.error(new Throwable("Api error")));
viewModel.fetchNews();
verify(observer).onChanged(NewsListViewState.LOADING_STATE);
verify(observer).onChanged(NewsListViewState.ERROR_STATE);
}
@After
public void tearDown() throws Exception {
apiClient = null;
viewModel = null;
}
InstantTaskExecutorRule
Ini akan memberi tahu JUnit untuk memaksa pengujian agar dieksekusi secara sinkron, terutama saat menggunakan Komponen Arsitektur. Bagaimana kami dapat memverifikasi bahwa ViewModels kami memicu peristiwa yang tepat untuk Tampilan kami?
Memverifikasi acara Pengamat onChanged()
Kami dapat menguji ViewModels kami dengan menggunakan mockito untuk memverifikasi bahwa pengamat kami onChanged() dipanggil ketika metode postValue() seharusnya dipicu dalam ViewModel kami.
Saya menggunakan mockito untuk mengejek dependensi. Jika Anda menghadapi masalah seperti mockito tidak bisa memata-matai kelas akhir maka gunakan testImplementation org.mockito:mockito-inline:2.13.0
di build.gradle.
ada 1 cara lagi jika Anda menggunakan org.mockito:mockito-core
lalu di dalam folder uji buat folder "sumber daya"
lalu di dalam folder sumber daya buat folder baru "mockito-extensions"
kemudian di dalam folder ini buat file “org.mockito.plugins.MockMaker” dengan konten “mock-maker-inline”