我有点困惑应该如何测试
我有这个存储库类:
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)
,并且我能够测试所有这些函数(这些函数均调用另一个类)。
这是唯一给我这个错误的人。
在此先感谢您的任何见识。
答案 0 :(得分:0)
mock()可能有错误。您确定数据源模拟具有实现返回方法延迟的实现吗?
.fetchAuthenticationFromServerAsync(username, password)
This medium post的示例包含一个模拟方法,该方法返回一个延迟值,这将为您的用例提供类似的信息:
//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()
答案 1 :(得分: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中的错误不会自动模拟内部功能。