首页 > 解决方案 > 如何在 Kotlin Android 中对异常进行单元测试

问题描述

我正在尝试编写一个单元测试来测试是否抛出异常,但我不完全确定如何做到这一点。

这是代码:

class GpsInfo {

    private lateinit var locationManager : LocationManager

    fun getGpsInfo(activity: Activity) : Location? {
        locationManager = activity.getSystemService(Context.LOCATION_SERVICE) as LocationManager

        var gpsCoordinates: Location
        try {
            gpsCoordinates = getGpsCoordinatesFromGpsProvider(locationManager)
        } catch(e: UnableAccessGPSProvider) {
            gpsCoordinates = getGpsCoordinatesFromNetwork(locationManager)
        } catch(e: Exception) {
            throw UnableGetGpsCoordinates("Cant get GPS coordinates")
        }

        return gpsCoordinates
    }

    private fun getGpsCoordinatesFromGpsProvider(locationManager: LocationManager) : Location {
        val gpsCoordinates = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER)
        if (gpsCoordinates == null) {
            throw UnableAccessGPSProvider("Cant get GPS coordinates from GPS Provider")
        }
        return gpsCoordinates
    }

    private fun getGpsCoordinatesFromNetwork(locationManager: LocationManager) : Location {
        val gpsCoordinates = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER)
        if (gpsCoordinates == null) {
            throw UnableAccessNetwork("Cant get GPS coordinates from network")
        }
        return gpsCoordinates
    }

在这里我悲伤的单元测试尝试,我试图通过一个模拟类来实现它:

import android.app.Activity
import io.mockk.every
import io.mockk.mockk
import org.junit.*
import kotlin.test.assertFailsWith

class GpsServiceUnitTest {
    private val gps = mockk<GpsInfo>()

    @Before
    fun iniz() {
        every { gps.getGpsInfo(Activity()) } throws UnableAccessGPSProvider("No GPS found")
    }

    @Test
    fun testFails() {
        assertFailsWith(UnableAccessGPSProvider::class) {
            gps.getGpsInfo(Activity())
        }
    }
}

我怎样才能做到这一点?我需要为函数提供一些东西,以便它会抛出异常,但我不知道这是如何工作的。

谢谢!

标签: androidunit-testingkotlinjunitmocking

解决方案


请避免嘲笑您尝试测试的课程,因为您想看到“真实”的行为。

我的第一步是稍微改变你的类并将字段移动locationManager到构造函数:

class GpsInfo(private val locationManager : LocationManager) {

    fun getGpsInfo(): Location? {
        var gpsCoordinates: Location
        try {
            gpsCoordinates = getGpsCoordinatesFromGpsProvider(locationManager)
        } catch (e: UnableAccessGPSProvider) {
            gpsCoordinates = getGpsCoordinatesFromNetwork(locationManager)
        } catch (e: Exception) {
            throw UnableGetGpsCoordinates("Cant get GPS coordinates")
        }

        return gpsCoordinates
    }

    private fun getGpsCoordinatesFromGpsProvider(locationManager: LocationManager): Location {
        val gpsCoordinates = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER)
        if (gpsCoordinates == null) {
            throw UnableAccessGPSProvider("Cant get GPS coordinates from GPS Provider")
        }
        return gpsCoordinates
    }

    private fun getGpsCoordinatesFromNetwork(locationManager: LocationManager): Location {
        val gpsCoordinates = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER)
        if (gpsCoordinates == null) {
            throw UnableAccessNetwork("Cant get GPS coordinates from network")
        }
        return gpsCoordinates
    }
}

现在您可以轻松地模拟该字段以使其抛出异常:

import com.nhaarman.mockito_kotlin.doThrow
import com.nhaarman.mockito_kotlin.mock
import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.catchThrowable

@Test
fun `should throw UnableGetGpsCoordinates exception`() {
    val locationManagerMock : LocationManager = mock {
        on { getLastKnownLocation(any()) } doThrow UnableAccessGPSProvider()
    }

    val throwable = catchThrowable { GpsInfo(locationManagerMock).getGpsInfo() }

    assertThat(throwable).isNotNull()
}

您也可以保留locationManager作为方法参数并模拟它以进行测试,但测试它需要更多的努力,因为我们将不得不模拟两次 - 活动和.getSystemService(Context.LOCATION_SERVICE)调用。

顺便说一句:我建议使用nhaarman 库进行模拟,使用assertJ 进行断言


推荐阅读