I am trying to implement unit test with Mockito for my View-Model, When my debugger is going to assign the value to live data it is crashing with error NullPointerException i.e. We are not bale to assign value to live data
My View-Model-Test Class
@Test
fun getNews() {
Mockito.`when`(mainRepository.getNews()).thenReturn(Observable.just(getMockResponse()))
//It is working good
Mockito.`when`(networkHelper.isNetworkConnected()).thenReturn(true)
//It is working good
viewModel.getNews()
assertEquals(Resource.success(getList()), viewModel.newsResponse.value)
}
My ViewModel
val newsResponse by lazy { SingleLiveEvent<Resource<List<RowsItem>>>() }
fun getNews() {
if (!networkHelper.isNetworkConnected()) { //It is true as we macked in Test Clas
newsResponse.value = Resource.error(AppError.NetworkError)
return
}
newsResponse.value = Resource.loading() // Crashing here when we are assigning value to live data: newsResponse
val api = mainRepository.getNews().map {
it.rows
}
val disposable = api.subscribeOn(Schedulers.computation())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{ result -> handleResults(result) },
{ error -> handleError(error) },
)
compositeDisposable.add(disposable)
}
private fun handleResults(list: ArrayList<RowsItem>?) {
if (list != null) {
newsResponse.value = Resource.success(list)
} else {
newsResponse.value = Resource.error(AppError.CommonError)
}
}
private fun handleError(t: Throwable?) {
newsResponse.value = Resource.error(t?.failureAppError() ?: AppError.CommonError)
}
Error
java.lang.NullPointerException at androidx.arch.core.executor.DefaultTaskExecutor.isMainThread(DefaultTaskExecutor.java:77) at androidx.arch.core.executor.ArchTaskExecutor.isMainThread(ArchTaskExecutor.java:116) at androidx.lifecycle.LiveData.assertMainThread(LiveData.java:486) at androidx.lifecycle.LiveData.setValue(LiveData.java:306) at androidx.lifecycle.MutableLiveData.setValue(MutableLiveData.java:50) at com.lifex.proficiencyexercise.repo.utils.SingleLiveEvent.setValue(SingleLiveEvent.java:28) at com.lifex.proficiencyexercise.ui.MainActivityViewModel.getNews(MainActivityViewModel.kt:37) at com.lifex.proficiencyexercise.ui.MainActivityViewModelTest.getNews(MainActivityViewModelTest.kt:45) 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:54) 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:99) at org.mockito.internal.runners.DefaultInternalRunner.run(DefaultInternalRunner.java:105) at org.mockito.internal.runners.StrictRunner.run(StrictRunner.java:40) at org.mockito.junit.MockitoJUnitRunner.run(MockitoJUnitRunner.java:163) at org.junit.runner.JUnitCore.run(JUnitCore.java:137) at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69) at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33) at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:220) at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:53)
Process finished with exit code -1
Just need to add rules in test class
@Rule
@JvmField
var testSchedulerRule = RxImmediateSchedulerRule() //For Retrofit
@get:Rule
var instantTaskExecutorRule = InstantTaskExecutorRule()//For LiveData
RxImmediateSchedulerRule is a custom class that extends TestRule
class RxImmediateSchedulerRule : TestRule {
private val immediateScheduler = object : Scheduler() {
@NonNull
override fun scheduleDirect(@NonNull run: Runnable, delay: Long, @NonNull unit: TimeUnit): Disposable {
// Hack to prevent stack overflows in unit tests when scheduling with a delay;
return super.scheduleDirect(run, 0, unit)
}
@NonNull
override fun createWorker(): Worker {
return ExecutorScheduler.ExecutorWorker(Executor { it.run() })
}
}
@NonNull
override fun apply(@NonNull base: Statement, @NonNull description: Description): Statement {
return object : Statement() {
@Throws(Throwable::class)
override fun evaluate() {
RxJavaPlugins.setInitIoSchedulerHandler { immediateScheduler }
RxJavaPlugins.setInitComputationSchedulerHandler { immediateScheduler }
RxJavaPlugins.setInitNewThreadSchedulerHandler { immediateScheduler }
RxJavaPlugins.setInitSingleSchedulerHandler { immediateScheduler }
RxAndroidPlugins.setInitMainThreadSchedulerHandler { immediateScheduler }
try {
base.evaluate()
} finally {
RxJavaPlugins.reset()
RxAndroidPlugins.reset()
}
}
}
}}
Now at the end My Test Class will look like
@RunWith(MockitoJUnitRunner::class)
class MainActivityViewModelTest : TestCase() {
private val networkHelper = Mockito.mock(NetworkHelper::class.java)
private val mainRepository = Mockito.mock(MainRepository::class.java)
private val viewModel = MainActivityViewModel(networkHelper, mainRepository)
@Rule
@JvmField
var testSchedulerRule = RxImmediateSchedulerRule() //For Retrofit
@get:Rule
var instantTaskExecutorRule = InstantTaskExecutorRule()//For LiveData
@Before
public override fun setUp() {
}
@Test
fun getNews() {
Mockito.`when`(mainRepository.getNews()).thenReturn(Observable.just(getMockResponse()))
//It is working good
Mockito.`when`(networkHelper.isNetworkConnected()).thenReturn(true)
//It is working good
viewModel.getNews()
assertEquals(Resource.success(getList()), viewModel.newsResponse.value)
}
private fun getMockResponse(): ApiResponse<ArrayList<RowsItem>> {
return ApiResponse("Response Success", getList())
}
private fun getList(): ArrayList<RowsItem> {
val rowsItem = RowsItem("ImageHref", "Desc", "Title")
val list = ArrayList<RowsItem>()
list.add(rowsItem)
return list
}}