Kotlin Coroutines MVVM Retrofit UnitTestCases — Part3

ChandraSaiMohan bhupathi
3 min readMay 23, 2020

This story explains about Kotlin Coroutines with MVVM Architecture and Retrofit with Sample examples.

App Flow :

The Coroutine sample that will be explained in the story contains 3 screens

(1) Login Screen

(2) List Screen

(3) List Detail Screen

(4)Corresponding Unit test cases with Mockito.

Used Libraries:

The below libraries are used as part of this implementation

Mockito : For unit testcases

Retrofit : For Making API calls

CoRoutines : Used in combination with Retrofit to make API calls in the separate thread.

Dagger : Dagger2 is used for Dependency Injection

MVVM : MVVM Architecture is followed to build the screens.

The below URL is used to make API calls in the above screens:

Detailed Explanation of Each of the screens:

(1) Login Screen : Contains user interface that display 2 fields Email and password which are pre-populated for the purpose of the tutorial.

When login button is clicked , post API call is performed to validate the entered credentials.

Login Screen

NetworkAPIService: The below interface contains list of suspend functions to make API calls .

@Post : For Login to validate login validate credentials by posting to Server

@GET : fun fetchUsers() Will fetch list of all users from API server

@GET: fun fetchSelectedUsers() : Will fetch information about selected user in List to display user details .

interface NetworkAPIService {

@POST("/api/login")
suspend fun validateLogin(@Body loginModel: LoginModel) : Response<TokenModel>

@GET("/api/users")
suspend fun fetchUsers(@Query("page") page :Int): Response<RetroResult>

@GET("/api/users/{id}")
suspend fun fetchSelectedUsers(@Path("id") id : Int): Response<RetroResultUser>
}

NetworkURL: This is the companion object class that contains information about the URL.

class NetworkURL {
companion object {
const val NETWORK_BASE_URL = "https://reqres.in"
const val REQUEST_TIMEOUT = 60L
}
}

ViewModels : These are the classes where business logic to handle API calls .

LoginViewModelFactory: This class injects Retrofit instance and it initialized corresponding view model with a Dispatcher.Main and networkAPIService as arguments .

Note: Dispatcher.Main to be passed to ViewModel , in order to unit test the view model.

class LoginViewModelFactory : ViewModelProvider.Factory {
@Inject
lateinit var retrofit: Retrofit
lateinit var networkAPIService: NetworkAPIService
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
DaggerNetworkComponent.create().inject(this)
networkAPIService = retrofit.create(NetworkAPIService::class.java)
if (modelClass.isAssignableFrom(LoginViewModel::class.java)) {
return LoginViewModel(Dispatchers.Main,networkAPIService) as T
}
throw IllegalArgumentException("Unknown ViewModel class")
}
}

ViewModel: (LoginViewModel)

This file contains all the logic related to posting login credential data to the API service for validation. This class uses Coroutines to make API calls .

View Model takes Dispatcher and NetworkService API as arguments:

class LoginViewModel(
private val dispatcher: CoroutineDispatcher,
private val apiService: NetworkAPIService
) : ViewModel(), LifecycleObserver

Sample Code for the function that makes API call:

fun validateLogin(loginModel: LoginModel){

viewModelScope.launch(dispatcher) {
try{
val response = apiService.validateLogin(loginModel)
if(response.isSuccessful) {
responseToken.postValue(response.body())
loading.postValue(false)
}else{
loading.postValue(false)

// println("Response Error body during failure is ${JSONObject(response.errorBody()?.string()).get("error")} ")
val errorMessage = JSONObject(response.errorBody()?.string()).get("error").toString()
println("Response Error Message $errorMessage")
errorOnAPI.postValue("$errorMessage")
}

}catch (e : Exception){
loading.postValue(false)
errorOnAPI.postValue("Something went wrong::${e.localizedMessage}")
}
}
}

UserList Screen:

This screen contains a Recycler View to fetch data from the API.

User List Screen

The above screen uses @Get service call to fetch data from the API.

UserDetail Screen : Launched when clicked on any of the list item.

User Detail Screen

GitHub Link for the code related to above screens:

--

--