Mock Apollo Client on the Kotlin Multiplatform

Hi apollo community :wave:,

I am using the experimental apollo client dependency for the Kotlin Multiplatform:

com.apollographql.apollo:apollo-runtime-kotlin:2.5.7

I really love this library it works very good and provides the functionality desired.

I am trying to write unit tests for my request classes and I want to mock the ApolloClient dependency. I read that one https://spectrum.chat/apollo/apollo-android/mocking-apollo-in-kotlin-multiplaform-mobile~935224f4-16db-41f5-92a4-6d9de4c14d54 and it helps me to understand how to mock the ApolloClient, thanks Martin for the answer there :+1:.

The only question left is, is there a possibility to mock some header into the apollo response? Can someone please help me here with an advice? I refer to this class here: https://github.com/apollographql/apollo-android/blob/main/apollo-runtime-kotlin/src/commonTest/kotlin/com/apollographql/apollo/mock/MockNetworkTransport.kt

@mbonnin maybe you have an idea here? Probably it is a small code change I need to do :grimacing:.

Hi! can you share a bit more about what header you want to mock?
ApolloResponse has a executionContext property that can contain a HttpExecutionContext: https://github.com/apollographql/apollo-android/blob/49d4768dc381f51acc38256d28a9dd99afea9f1b/apollo-runtime-kotlin/src/jvmMain/kotlin/com/apollographql/apollo/network/http/ApolloHttpNetworkTransport.kt#L131

Would that work?

Hi @mbonnin

thanks a lot for getting back to me regarding my question.

I am using exactly that MockNetworkTransport from GitHub from your sample. I need to mock the ApolloResponse for my unit tests. That “mocked behavior” I put to an ApolloClient for my tests. My class which mocks the apollo client looks like this atm:

class ApolloRequestClientMock {

    private var networkTransport: MockNetworkTransport = MockNetworkTransport()

    fun simpleApolloClient(headers: Map<String, String>?): ApolloClient {
        return ApolloClient(
            networkTransport = networkTransport,
            // The 'TestLoggerExecutor' is the one from GitHub 
            // https://github.com/apollographql/apollo-android/blob/main/apollo-runtime-kotlin/src/commonTest/kotlin/com/apollographql/apollo/mock/TestLoggerExecutor.kt
            interceptors = listOf(TestLoggerExecutor)
        )
    }

    fun setExpectedSuccessForNetworkTransportWith(responseJson: String) {
        networkTransport.trySend(responseJson).isSuccess
    }

    fun setExpectedFailureForNetworkTransportBecauseDataEmpty() {
        networkTransport.trySend("").isFailure
    }

    fun setExpectedFailureForNetworkTransportWith(failureResponse: String) {
        networkTransport.trySend(failureResponse).isFailure
    }
}

If the ApolloResponse does not contain a header named __typename I receive this error when executing my tests:

java.lang.NullPointerException: corrupted response reader, expected non null value for __typename

To have this __typename header I added a little bit of code to the MockNetworkTransport, regarding your answer and now the class is looking like this

@ApolloExperimental
@ExperimentalCoroutinesApi
internal class MockNetworkTransport(
    private val mockResponseChannel: Channel<String> = Channel(capacity = Channel.BUFFERED)
) : NetworkTransport, SendChannel<String> by mockResponseChannel {

    private var expectedStatusCode: Int = 200
    private var expectedHeaders: Map<String, String> = mapOf("__typename" to "some-typename")

    override fun <D : Operation.Data> execute(
        request: ApolloRequest<D>,
        executionContext: ExecutionContext
    ): Flow<ApolloResponse<D>> {
        return flow {
            emit(
                ApolloResponse(
                    requestUuid = request.requestUuid,
                    response = request.operation.parse(mockResponseChannel.receive().encodeUtf8()),
                    executionContext = request.executionContext + HttpExecutionContext.Response(
                        statusCode = expectedStatusCode,
                        headers = expectedHeaders
                    )
                )
            )
        }
    }

    fun setExpected(statusCode: Int) {
        expectedStatusCode = statusCode
    }
}

But it still does not work, I receive the same error of missing __typename. Is there anything I can change in my code to make it work?

I fixed the issue. It wasn’t about the header :woman_facepalming:. This __typename should be in the response body and not in the header - for some reasons I thought that this field belongs in the header. Beginner issue.

So this one is solved. With the sample mock code from Martin writing unit tests for amp by mocking the ApolloClient works great :+1:

1 Like

@rb090 yep, exactly. __typename is a graphql field as you found out :+1:.
Thanks for writing what I think is the first Android post on these forums :smiley:!

1 Like