Reputation: 455
I'm trying to run a unit test on my ViewModel class but when I run the test I am getting a NullPointerException. getDataManager().getAuthToken() method call an API using retrofit and return a string response. Is there any way to test the method 'startLogin()'? Here is the code.
LoginViewModelTest.kt
@RunWith(JUnit4::class)
class LoginViewModelTest {
@Rule
@JvmField
var instantTaskExecutorRule = InstantTaskExecutorRule()
companion object {
@ClassRule
@JvmField
val schedulers = RxSchedulerRule()
}
private val application = mock(Application::class.java)
private val dataManager = mock(DataManager::class.java)
private val serviceConnector = mock(ServiceConnector::class.java)
private val requestInterceptor = mock(RequestInterceptor::class.java)
private lateinit var compositeDisposable: CompositeDisposable
private lateinit var loginViewModel: LoginViewModel
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
compositeDisposable = CompositeDisposable()
loginViewModel = LoginViewModel(application, dataManager, serviceConnector, compositeDisposable, requestInterceptor)
}
@Test
fun testLoginWithValidQR(){
val map = QueryMapBuilder.getAuthTokenHeaders()
val body = TokenReqBody()
val success = TokenResSuccess()
`when`(dataManager.getAuthToken(map, body)).thenReturn(Observable.just(success))
assertNotNull(dataManager.getAuthToken(map, body))
// got error here
loginViewModel.startLogin()
}
}
LoginViewModel.java
public class LoginViewModel extends BaseViewModel {
private RequestInterceptor mRequestInterceptor;
private SingleLiveEvent<LoginViewDataEvents> loginViewDataSingleLiveEvent;
@Inject
public LoginViewModel(Application application, DataManager dataManager, ServiceConnector serviceConnector, CompositeDisposable compositeDisposable, RequestInterceptor requestInterceptor) {
super(application, dataManager, serviceConnector, compositeDisposable);
mRequestInterceptor = requestInterceptor;
loginViewDataSingleLiveEvent = new SingleLiveEvent<>();
}
public void startLogin() {
Map<String, String> map = QueryMapBuilder.getAuthTokenHeaders();
TokenReqBody body = QueryMapBuilder.getAuthTokenBody(AppConfig.getConfig());
getCompositeDisposable().add(getDataManager().getAuthToken(map, body)
.subscribeOn(Schedulers.io()) //getting error here
.observeOn(AndroidSchedulers.mainThread())
.subscribe()
);
}
}
Error Logs
java.lang.NullPointerException
at com.example.doc.ui.login.LoginViewModel.startLogin(LoginViewModel.java:102)
at com.example.doc.login.LoginViewModelTest.testLoginWithValidQR(LoginViewModelTest.kt:86)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
at org.junit.rules.TestWatcher$1.evaluate(TestWatcher.java:55)
at org.junit.rules.RunRules.evaluate(RunRules.java:20)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at com.example.doc.RxSchedulerRule$apply$1.evaluate(RxSchedulerRule.kt:44)
Upvotes: 1
Views: 1536
Reputation: 988
The issue is, in unit test you are creating a new object for DataManager
private val dataManager = mock(DataManager::class.java)
and using that to call dataManager.getAuthToken(map, body);
However, in actual implementation inside LoginViewModel, you use the below line,
getDataManager().getAuthToken(map, body)
Here getDataManager() is null. So you need to add below line make it work,
when(getDataManager()).thenReturn(dataManager);
Upvotes: 1
Reputation: 141
The reason for getting null inside startLogin
function is because java passes non-primitive variables by reference, meaning that it passes memory address and not the value itself.
The first use of dataManager.getAuthToken(map, body)
passes because you are using the same objects map
and body
that defined the 'when'
statement (they have the same memory address).
The second time (inside the function), you are using entirely new objects that have a new memory address so the 'when'
statement does not trigger.
A solution for this is to change the 'when'
statement to:
`when`(dataManager.getAuthToken(any(), any())).thenReturn(Observable.just(success))
Which means that the statement will be triggered with any object passed to the function.
Upvotes: 4