Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Method getMainLooper in android.os.Looper not mocked still occuring even after adding RxImmediateSchedulerRule

TrendingViewModelTest

@RunWith(JUnit4::class)
class TrendingViewModelTest {
    private lateinit var trendingRepository: TrendingRepository
    private lateinit var trendingViewModel: TrendingViewModel

    @get:Rule
    val schedulers = RxImmediateSchedulerRule()

    @Before
    fun setUp() {
        trendingRepository = mock(TrendingRepository::class.java)
        trendingViewModel = TrendingViewModel(trendingRepository)
    }

    @Test
    fun testWithNetwork() {
        trendingViewModel.isConnected = true
        trendingViewModel.fetchTrendingRepos()
        verify(trendingRepository, times(1)).getTrendingRepos()
    }

    //...
}

TrendingViewModel

fun fetchTrendingRepos() {
if (isConnected) {
    loadingProgress.value = true
    compositeDisposable.add(
        trendingRepository.getTrendingRepos().subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe({ response ->
                run {
                    loadingProgress.value = false
            },
                { error ->
                    loadingProgress.value = false
                }
            )
    )
} 

RxImmediateSchedulerRule:

class RxImmediateSchedulerRule : TestRule {
    override fun apply(base: Statement?, description: Description?): Statement {
        return object : Statement() {
            @Throws(Throwable::class)
            override fun evaluate() {
                RxJavaPlugins.setIoSchedulerHandler { Schedulers.trampoline() }
                RxJavaPlugins.setComputationSchedulerHandler { Schedulers.trampoline() }
                RxJavaPlugins.setNewThreadSchedulerHandler { Schedulers.trampoline() }
                RxAndroidPlugins.setInitMainThreadSchedulerHandler { Schedulers.trampoline() }

                try {
                    base?.evaluate()
                } finally {
                    RxJavaPlugins.reset()
                    RxAndroidPlugins.reset()
                }
            }
        }
    }
}

TrendingRepositoryImpl:

class TrendingRepositoryImpl @Inject constructor(
    val apiService: GitHubApi,
    val trendingDao: AppDao
) : TrendingRepository {

    override fun getTrendingRepos(): Single<List<TrendingRepo>> {
        return apiService.getTrendingGits()
    }
}

TrendingRepository:

interface TrendingRepository {
    fun getTrendingRepos(): Single<List<TrendingRepo>>
}

Inside fetchTrendingRepos() an Rxjava call is initiated, it also hooks onto 'AndroidSchedulers.mainThread()' that might be the reason causing it.

java.lang.RuntimeException: Method getMainLooper in android.os.Looper not mocked. at android.os.Looper.getMainLooper(Looper.java) 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:461) at androidx.lifecycle.LiveData.setValue(LiveData.java:304) at androidx.lifecycle.MutableLiveData.setValue(MutableLiveData.java:50) at com.manoj.trendgitz.mvvm.ui.TrendingViewModel.fetchTrendingRepos(TrendingViewModel.kt:32) at com.manoj.trendgitz.TrendingViewModelTest.testWithNetwork(TrendingViewModelTest.kt:52) 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.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 org.junit.runners.ParentRunner.run(ParentRunner.java:363) at org.junit.runner.JUnitCore.run(JUnitCore.java:137) at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68) at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47) at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242) at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)

like image 668
Manoj Perumarath Avatar asked Sep 23 '19 07:09

Manoj Perumarath


2 Answers

As you update LiveData value, you should add @get:Rule var rule: TestRule = InstantTaskExecutorRule() also.

Don't forget to add following to build.gradle file:

dependencies {
    // ...
    testImplementation "androidx.arch.core:core-testing:2.1.0"
}

Also, change your test code accordingly to avoid NullPointerException:

@Test
fun testWithNetwork() {
    trendingViewModel.isConnected = true
    Mockito.`when`(trendingRepository.fetchTrendingRepos()).thenReturn(Single.just(listOf<TrendingRepo>()))
    trendingViewModel.fetchTrendingRepos()
    verify(trendingRepository, times(1)).getTrendingRepos()
}

Mockito.when() lets you to do different actions every time your mock method is called. If you don't use it, you may see possible NullPointerException depending on your test function.

like image 175
Natig Babayev Avatar answered Sep 20 '22 02:09

Natig Babayev


Add the below lines under android {} in your app's build.gradle

testOptions {
            // Used for Unit testing Android dependent elements in /test folder
            unitTests.includeAndroidResources  = true
            unitTests.returnDefaultValues = true
        }
like image 23
Kanagalingam Avatar answered Sep 19 '22 02:09

Kanagalingam