androidunit-testingkotlin

Problems with Kotlin Result<T> on unit tests


I am working on an android Application and I opted to use Kotlin Result class so as to handle success/failure on my operations. I made the changes to the code, but the tests stop working and I cannot understand why. Here I show you some snippets:

FireStoreClient.kt

suspend fun items(): Result<ItemsResponse>

NetworkDataSource.kt

suspend fun getItems(): List<Item> =
    fireStoreClient.items().fold({ it.items.map { item -> item.toDomain() } }, { emptyList() })

NetworkDataSourceTest.kt

@ExperimentalCoroutinesApi
@Test
fun `Check getItems works properly`() = runBlockingTest {
    whenever(fireStoreClient.items()).doReturn(success(MOCK_ITEMS_DOCUMENT))
    val expectedResult = listOf(
        Item(
            id = 1,
            desc = "Description 1"
        ),
        Item(
            id = 2,
            desc = "Description 2"
        )
    )
    assertEquals(expectedResult, dataSource.getItems())
}

And this is the exception I am getting right now. Any clue? It appears that the fold() method is not being executed when unit testing.

java.lang.ClassCastException: kotlin.Result cannot be cast to ItemsResponse

    at NetworkDataSource.getItems(NetworkDataSource.kt:31)

Solution

  • I had the same issue.

    I noticed that my method of injected class which should return Result<List<Any>> returns actually Result<Result<List<Any>>> which causes the ClassCastException. I used the Evaluate Expression option for the result from the method and I got

    Success(Success([]))
    

    The app works well but unit tests didn't pass due this problem.

    As a temporary solution I built a new simple implementation of Result sealed class with fold() extension function. It should be easy to replace in future to kotlin.Result

    Result sealed class:

    sealed class Result<T> {
        data class Success<T>(val value: T) : Result<T>()
        data class Failure<T>(val error: Throwable) : Result<T>()
    }
    

    fold() extension function:

    inline fun <R, T> Result<T>.fold(
        onSuccess: (value: T) -> R,
        onFailure: (exception: Throwable) -> R
    ): R = when (this) {
        is Result.Success -> onSuccess(value)
        is Result.Failure -> onFailure(error)
    }