3

我有点困惑我应该如何测试这个

我有这个存储库类:


class AuthenticationBox(
    private val dataMapper: AuthenticationDataMapper,
    private val dataSource: AuthenticationDataSource
) : BaseRepository<UserAuthentication>(), AuthenticationRepository {

override suspend fun fetchAuthenticationFromServer(
        username: String,
        password: String
    ): ResourceResult<UserAuthentication>? {
        var result: ResourceResult<UserAuthentication>? = null

        withContext(Dispatchers.IO) {
            try {
                val response = dataSource
                    .fetchAuthenticationFromServerAsync(username, password).await()

                if (response.hasErrors()) {
                    result =
                        ResourceResult.Error(ApolloException(response.errors()[0].message()))

                } else {
                    response.data()?.let {
                        val authorization = dataMapper.toDomain(it.loginResponse)

                        result = ResourceResult.Success(authorization)
                    }

                }

            } catch (error: ApolloException) {
                result = handleAuthenticationError(error)
            }
        }
        return result

    }


}

这一行调用了一个数据源方法,该方法返回一个Deferred<Response>对象并等待它 val response = dataSource.fetchAuthenticationFromServerAsync(username, password).await()

当我尝试测试它时,我总是NullPointerException在这一行得到一个完全正确的
这是我的测试类:

class AuthenticationBoxTest {

    lateinit var repository: AuthenticationRepository

    var dataSourceMock: AuthenticationDataSource = mock()
    var dataMapperMock: AuthenticationDataMapper = mock()


   @Before
    fun setup() {
        repository = AuthenticationBox(
            dataMapper = dataMapperMock,
            dataSource = dataSourceMock
        )
    }

     @Test
    fun testFromServer() {
        val username = "username"
        val password = "password"
        runBlocking {
            repository.fetchAuthenticationFromServer(username, password)
        }
    }

}

日志输出为:

java.lang.NullPointerException
    at br.com.ampli.authentication.repository.AuthenticationBox$fetchAuthenticationFromServer$2.invokeSuspend(AuthenticationBox.kt:45)
    at |b|b|b(Coroutine boundary.|b(|b)
    at br.com.ampli.authentication.repository.AuthenticationBox.fetchAuthenticationFromServer(AuthenticationBox.kt:42)
    at br.com.ampli.authentication.repository.AuthenticationBoxTest$testFromServer$1.invokeSuspend(AuthenticationBoxTest.kt:126)
Caused by: java.lang.NullPointerException
    at br.com.ampli.authentication.repository.AuthenticationBox$fetchAuthenticationFromServer$2.invokeSuspend(AuthenticationBox.kt:45)
    at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
    at kotlinx.coroutines.DispatchedTask.run(Dispatched.kt:241)
    at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:594)
    at kotlinx.coroutines.scheduling.CoroutineScheduler.access$runSafely(CoroutineScheduler.kt:60)
    at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:740)


我在这个存储库类上的所有其他函数都是挂起的函数和所有用途withContext(Dispatchers.IO),我能够全部测试它们(其中任何一个调用另一个类)。这是唯一给我这个错误的。

提前感谢您对此的任何见解。

4

2 回答 2

1

你的 mock() 可能有错误。您确定数据源模拟具有返回该方法的延迟的实现吗?

.fetchAuthenticationFromServerAsync(username, password)

这篇中型帖子有一个示例,其中包含一个返回延迟的模拟方法,这将为您的用例提供类似的内容:

//a small helper function first
fun <T> T.toDeferred() = GlobalScope.async { this@toDeferred }

val authResult = dummyAuthDataSourceResult  //dummy result
val mockedDatasource = mock<AuthenticationDataSource> {    
      on { fetchAuthenticationFromServerAsync() } doReturn authResult.toDeferred()
}

现在您应该可以安全地调用:

mockedDatasource.fetchAuthenticationFromServerAsync().await()
于 2019-10-25T09:17:51.397 回答
0
val deferred: Deferred = mock()

@Before
fun setup() {
    doNothing().whenever(deferred.await())
    whenever(dataSource.fetchAuthenticationFromServerAsync()).doReturn(deferred)

    repository = AuthenticationBox(
        dataMapper = dataMapperMock,
        dataSource = dataSourceMock
    )
}

然后在您的测试中,您可以执行以下操作:

@Test
fun testFromServer() {
    val username = "username"
    val password = "password"
    runBlocking {
        repository.fetchAuthenticationFromServer(username, password)
    }
    verify(dataSource).fetchAuthenticationFromServerAsync() // Verify fun called
}

你必须模拟几乎所有的模拟行为。老实说,我不知道这是模拟的预期行为,还是 kotlin 中的一个错误,它不会自动模拟内部函数。

于 2019-10-29T10:04:44.697 回答