Android 中的单元测试 isNetworkAvailable()

Unit test isNetworkAvailable() in Android

我有以下单元测试:

    @Test
    fun givenServerResponse200_whenFetch_shouldReturnSuccess() {
        `when`(context.isNetworkAvailable()).thenReturn(true)
        `when`(api.fetchShowList()).thenReturn(Calls.response(Response.success(emptyList())))
        `when`(dao.getShows()).thenReturn(flowOf(emptyList()))
        val repository = ShowRepository(dao, api, context, TestContextProvider())
        val viewModel = MainViewModel(repository)
        viewModel.shows.observeForever(resource)
        try {
            verify(resource).onChanged(Resource.loading())
            verify(resource).onChanged(Resource.success(emptyList()))
        } finally {
            viewModel.shows.removeObserver(resource)
        }
    }

这是 isNetworkAvailable 方法:

@Suppress("DEPRECATION")
fun Context.isNetworkAvailable(): Boolean {
    val connectivityManager =
        getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        val nw = connectivityManager.activeNetwork ?: return false
        val actNw = connectivityManager.getNetworkCapabilities(nw) ?: return false
        return when {
            actNw.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> true
            actNw.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> true
            //for other device how are able to connect with Ethernet
            actNw.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) -> true
            //for check internet over Bluetooth
            actNw.hasTransport(NetworkCapabilities.TRANSPORT_BLUETOOTH) -> true
            else -> false
        }
    } else {
        val nwInfo = connectivityManager.activeNetworkInfo ?: return false
        return nwInfo.isConnected
    }
}

这是我收到的异常消息:

kotlin.TypeCastException: null cannot be cast to non-null type android.net.ConnectivityManager

    at com.android.sample.tvmaze.util.ContextExtKt.isNetworkAvailable(ContextExt.kt:14)
    at com.android.sample.tvmaze.MainViewModelTest.givenServerResponse200_whenFetch_shouldReturnSuccess(MainViewModelTest.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.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 com.android.sample.tvmaze.TestCoroutineRule$apply.evaluate(TestCoroutineRule.kt:22)
    at org.junit.rules.TestWatcher.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.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access[=13=]0(ParentRunner.java:58)
    at org.junit.runners.ParentRunner.evaluate(ParentRunner.java:268)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.junit.runners.Suite.runChild(Suite.java:128)
    at org.junit.runners.Suite.runChild(Suite.java:27)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access[=13=]0(ParentRunner.java:58)
    at org.junit.runners.ParentRunner.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.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
    at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:230)
    at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:58)

有解决办法吗?

源代码位于:https://github.com/AliRezaeiii/TVMaze-Cache

kotlin.TypeCastException: null cannot be cast to non-null type android.net.ConnectivityManager

您对 isNetworkAvailable() 方法的模拟未正确模拟。所以 val connectivityManager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager 这个方法正在执行,你得到这个异常。

使用mockk to mock extension function. For details check this answer.