首页 > 解决方案 > UnitTest 中的 KotlinNullPointerException

问题描述

我有以下 ViewModel 类:

class MovieViewModel @JvmOverloads constructor(
        dataSource: MoviesRemoteDataSource,
        sortType: SortType? = null) : ViewModel() {

    // thread pool used for network requests
    @Suppress("PrivatePropertyName")
    private val NETWORK_IO = Executors.newFixedThreadPool(5)

    private val query = MutableLiveData<String>()
    private val repoResult = map(query) {
        PageKeyRepository(
                dataSource = dataSource,
                sortType = sortType,
                networkExecutor = NETWORK_IO).getMovies(it, 20)
    }

    val movies = switchMap(repoResult) { it.pagedList }!!
    val networkState = switchMap(repoResult) { it.networkState }!!
    val refreshState = switchMap(repoResult) { it.refreshState }!!

    fun refresh() {
        repoResult.value?.refresh?.invoke()
    }

    fun showQuery(query: String?): Boolean {
        if (this.query.value == query) {
            return false
        }
        this.query.value = query
        return true
    }
}

当我调用showQuery("")方法时,它会在我的 RecyclerView 中加载数据:

model.movies.observe(this@MainFragment, Observer<PagedList<Movie>> {
                adapter.submitList(it)
})

现在我有一个 unitTest 如下:

@Test
    fun loadMovies() {
        viewModel = MovieViewModel(dataSource, SortType.MOST_POPULAR)

        val voteAverage = 6.5
        val movie = Movie("id", "overview", "date",
                null, null, "title", voteAverage)

        val mockCall = Calls.response(Response.success(MovieApi.MovieWrapper(Lists.newArrayList(movie))))
        `when`(movieApi.popularMovies(1)).thenReturn(mockCall)

        with(viewModel) {
            showQuery("")

            assertTrue(networkState.value?.status == Status.SUCCESS)

            with(movies.value!!) {
                assertFalse(isEmpty())
                assertTrue(size == 1)
                assertTrue(this[0]?.voteAverage == voteAverage)
            }
        }
    }

我收到KotlinNullPointerExceptionnetworkState.valuemovies.value

kotlin.KotlinNullPointerException
    at com.sample.android.tmdb.base.MovieViewModelTest.loadMovies(MovieViewModelTest.kt:56)
    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.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 org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.mockito.internal.runners.DefaultInternalRunner$1.run(DefaultInternalRunner.java:79)
    at org.mockito.internal.runners.DefaultInternalRunner.run(DefaultInternalRunner.java:85)
    at org.mockito.internal.runners.StrictRunner.run(StrictRunner.java:39)
    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: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)

感谢您的帮助。源代码可以在:https ://github.com/Ali-Rezaei/TMDb-Paging

附录:

我尝试了以下测试,它按预期工作:

val repository = PageKeyRepository(dataSource, SortType.MOST_POPULAR, networkExecutor)

        val movie = Movie("id", "overview", "date",
                null, null, "title", voteAverage)

        val mockCall = Calls.response(Response.success(
                MovieApi.MovieWrapper(Lists.newArrayList(movie))))
        `when`(movieApi.popularMovies(1)).thenReturn(mockCall)

        val listing = repository.getMovies("", 20)
        val observer = LoggingObserver<PagedList<Movie>>()
        listing.pagedList.observeForever(observer)
        assertThat(observer.value, `is`(notNullValue()))
        val pagedList= observer.value!!
        assertThat(pagedList.size, `is`(1))

和 LoggingObserver:

/**
     * simple observer that logs the latest value it receives
     */
    private class LoggingObserver<T> : Observer<T> {
        var value: T? = null
        override fun onChanged(t: T?) {
            this.value = t
        }
    }

但我仍然不知道如何测试:

with(viewModel) {
                showQuery("")

                assertTrue(networkState.value?.status == Status.SUCCESS)

                with(movies.value!!) {
                    assertFalse(isEmpty())
                    assertTrue(size == 1)
                    assertTrue(this[0]?.voteAverage == voteAverage)
                }
            }

标签: androidunit-testingkotlinretrofit2

解决方案


推荐阅读