I created a view model to handle authentication (login and register), but I’m having trouble writing unit testing code. Can anyone help me write unit testing code for the view model below?
Authentication View Model:
class AuthenticationViewModel(private val userRepository: UserRepository) : ViewModel() {
var user = MutableLiveData<Event<UserModel>>()
var loading = MutableLiveData<Event<Boolean>>()
var message = MutableLiveData<Event<String>>()
var error = MutableLiveData<Event<Boolean>>()
fun userLogin(email: String, password: String): MutableLiveData<Event<UserModel>> {
loading.value = Event(true)
val client = userRepository.userLogin(email, password)
client.enqueue(object : Callback<UserResponse> {
override fun onResponse(
call: Call<UserResponse>,
response: Response<UserResponse>
) {
Log.e("AuthenticationViewModel", "onResponse: " + response.body())
loading.value = Event(false)
if (response.isSuccessful) {
val responseBody = response.body()?.loginResult
error.value = Event(false)
userRepository.appExecutors.networkIO.execute {
Log.e("AuthenticationViewModel", "userLogin: $responseBody")
user.postValue(Event(responseBody!!))
}
} else {
Log.e("AuthenticationViewModel", "onResponse fail: ")
message.value = Event(response.message())
error.value = Event(true)
}
}
override fun onFailure(call: Call<UserResponse>, t: Throwable) {
Log.e("AuthenticationViewModel", "onFailure: " + t.message)
loading.value = Event(false)
message.value = Event(t.message.toString())
error.value = Event(true)
}
})
return user
}
fun userRegister(
name: String,
email: String,
password: String
) {
loading.value = Event(true)
val client = userRepository.userRegister(name, email, password)
client.enqueue(object : Callback<UserResponse> {
override fun onResponse(
call: Call<UserResponse>,
response: Response<UserResponse>
) {
Log.e("AuthenticationViewModel", "onResponse: " + response.body())
loading.value = Event(false)
if (response.isSuccessful) {
error.value = Event(false)
} else {
Log.e("AuthenticationViewModel", "onResponse fail: ")
message.value = Event(response.message())
error.value = Event(true)
}
}
override fun onFailure(call: Call<UserResponse>, t: Throwable) {
Log.e("AuthenticationViewModel", "onFailure: " + t.message)
loading.value = Event(false)
message.value = Event(t.message.toString())
error.value = Event(true)
}
})
}
fun getUserToken() = userRepository.getUserToken()
fun saveUserToken(token: String?) {
viewModelScope.launch {
if (token != null) {
userRepository.saveUserToken(token)
}
}
}
fun saveUserName(name: String?) {
viewModelScope.launch {
if (name != null) {
userRepository.saveUserName(name)
}
}
}
fun saveUserEmail(email: String) {
viewModelScope.launch {
userRepository.saveUserEmail(email)
}
}
fun logout() {
viewModelScope.launch {
userRepository.clearCache()
}
}
}
I’ve tried to write unit testing code, but failed and encountered a NullPointerException
AuthenticationViewModelTest :
@RunWith(MockitoJUnitRunner::class)
class AuthenticationViewModelTest {
@get:Rule
var instantExecutorRule = InstantTaskExecutorRule()
@Mock
private lateinit var repository: UserRepository
private lateinit var viewModel: AuthenticationViewModel
private val dummyUser = DataDummy.generateDummyUser()
@Before
fun setUp() {
viewModel = AuthenticationViewModel(repository)
}
@Test
fun `when userLogin should return user response when email and password are valid and error is false`() {
val email = DataDummy.userEmail
val password = DataDummy.userPassword
val expectedUser = MutableLiveData<Event<UserModel>>()
expectedUser.value = Event(dummyUser)
val expectedError = MutableLiveData<Event<Boolean>>()
expectedError.value = Event(false)
`when`(viewModel.userLogin(email, password)).thenReturn(expectedUser)
val actualUser = viewModel.userLogin(email, password).getOrAwaitValue()
val actualError = viewModel.error.getOrAwaitValue()
Mockito.verify(repository).userLogin(email, password)
assertNotNull(actualUser.peekContent())
assertFalse(actualError.peekContent())
}
}
Error Exception
java.lang.NullPointerException
at com.mankart.mankgram.ui.authentication.AuthenticationViewModel.userLogin(AuthenticationViewModel.kt:32)
at com.mankart.mankgram.ui.authentication.AuthenticationViewModelTest.when userLogin should return user response when email and password are valid and error is false(AuthenticationViewModelTest.kt:46)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.mockito.internal.runners.DefaultInternalRunner$1$1.evaluate(DefaultInternalRunner.java:55)
at org.junit.rules.TestWatcher$1.evaluate(TestWatcher.java:61)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
at org.mockito.internal.runners.DefaultInternalRunner$1.run(DefaultInternalRunner.java:100)
at org.mockito.internal.runners.DefaultInternalRunner.run(DefaultInternalRunner.java:107)
at org.mockito.internal.runners.StrictRunner.run(StrictRunner.java:41)
at org.mockito.junit.MockitoJUnitRunner.run(MockitoJUnitRunner.java:163)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.runTestClass(JUnitTestClassExecutor.java:110)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:58)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:38)
at org.gradle.api.internal.tasks.testing.junit.AbstractJUnitTestClassProcessor.processTestClass(AbstractJUnitTestClassProcessor.java:62)
at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:51)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33)
at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:94)
at com.sun.proxy.$Proxy2.processTestClass(Unknown Source)
at org.gradle.api.internal.tasks.testing.worker.TestWorker$2.run(TestWorker.java:176)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.executeAndMaintainThreadName(TestWorker.java:129)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:100)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:60)
at org.gradle.process.internal.worker.child.ActionExecutionWorker.execute(ActionExecutionWorker.java:56)
at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:133)
at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:71)
at worker.org.gradle.process.internal.worker.GradleWorkerMain.run(GradleWorkerMain.java:69)
at worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:74)
com.mankart.mankgram.ui.authentication.AuthenticationViewModelTest > when userLogin should return user response when email and password are valid and error is false FAILED
java.lang.NullPointerException at AuthenticationViewModelTest.kt:46
1 test completed, 1 failed
> Task :app:testDebugUnitTest FAILED
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':app:testDebugUnitTest'.
> There were failing tests. See the report at: file:///C:/Users/asus/AndroidStudioProjects/Submission/Mankgram/app/build/reports/tests/testDebugUnitTest/index.html
* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.
* Get more help at https://help.gradle.org
BUILD FAILED in 2m 29s