Репозиторий тестирования Android с макетом и сопрограммой возвращает исключение NullPointerException - PullRequest
0 голосов
/ 10 октября 2019

Я немного смущен тем, как я должен проверить это

У меня есть этот класс репозитория:


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), и я могу проверить их все (любой из этих вызовов другого класса). Это единственная ошибка, которая дает мне эту ошибку.

Заранее благодарим за понимание этого вопроса.

Ответы [ 2 ]

0 голосов
/ 29 октября 2019
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, которая автоматически не насмехается над внутренними функциями.

0 голосов
/ 25 октября 2019

Может быть ошибка с вашим макетом (). Вы уверены, что макет источника данных имеет реализацию, которая возвращает отложенный метод?

.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()
...