Search code examples
androidmvvmjunit4android-viewmodel

How do I test a ViewModel class which has an interface dependecy that is generated by android as a Unit Test


Good day all, am trying to test my ViewModel class and it has a dependency of datasource, I tried to mock this, but it won't work because it's an interface, I believe the interface implementation is generated at runtime, how do I unit test this class, below is my ViewModel class

class LoginViewModel @ViewModelInject constructor(@ApplicationContext private val context: Context,
                                                 private val networkApi: NetworkAPI,
                                                 private val dataStore: DataStore<Preferences>)
   : ViewModel() {


   val clientNumber = MutableLiveData<String>()
   val clientPassword = MutableLiveData<String>()

   private val _shouldNavigate = MutableLiveData(false)
   val shouldNavigate: LiveData<Boolean>
       get() = _shouldNavigate

   private val _errorMessage = MutableLiveData<String>()
   val errorMessage: LiveData<String>
       get() = _errorMessage

private val _activateDeviceButton = MutableLiveData(false)
   val activateButton : LiveData<Boolean>
   get() = _activateDeviceButton

   init {
       populateApiWithFakeData()
   }


   suspend fun authenticateUsers(): Boolean {
       val clientNumber = clientNumber.value
       val clientPassword = clientPassword.value
       requireNotNull(clientNumber)
       requireNotNull(clientPassword)
       val (userExist, token) = networkApi.doesUserExist(clientNumber.toLong(), clientPassword)
       if (token.isNotBlank()) storeTokenInStore(token)
       return if (userExist) {
           true
       } else {
           _errorMessage.value = "Incorrect account details. Please try again with correct details"
           false
       }
   }

  private suspend fun storeTokenInStore(token: String) {
       dataStore.edit { pref ->
           pref[TOKEN_PREFERENCE] = token
       }
   }


and here is my ViewModel Test class

@Config(sdk = [Build.VERSION_CODES.O_MR1])
@RunWith(AndroidJUnit4::class)
class LoginViewModelTest{


    private val context : Context = ApplicationProvider.getApplicationContext()

    private val dataCentre = NetworkApImpl()

    @Mock
    private lateinit var dataStore: DataStore<Preferences>

    @Before
    fun setUpDataCenters(){
        val loginData = DataFactory.generateLoginData()
        for (data in loginData){
            dataCentre.saveUserData(data)
        }
    }

    @After
    fun tearDownDataCenter(){
        dataCentre.clearDataSet()
    }

    @Test
    @ExperimentalCoroutinesApi
    fun authenticateUser_shouldAuthenticateUsers(){
        //Given
        val viewModel = LoginViewModel(context, dataCentre, dataStore)
        viewModel.clientNumber.value = "8055675745"
        viewModel.clientPassword.value = "robin"
        //When
        var result : Boolean? = null
        runBlocking {
            result = viewModel.authenticateUsers()
        }
        //Then
        Truth.assertThat(result).isTrue()
    }

Any assistance rendered will be appreciated.


Solution

  • You can wrap your dependency in a class you own as Mockito suggests here. This also has the upside of letting you change your storage implementation latter without having and impact on every view model using it.