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)
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.
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
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With