0

I'm trying to test a function like this:

override suspend fun create(item: Location): FlowResult<Location> {
    val (base, version) = locationRepositoryMapper.map(item)
    return locationRemoteSource.create(base, version)
        .flatMapConcat {
            when (it) {
                is Result.Success -> {
                    locationLocalSource.create(
                        base = it.data.first,
                        version = it.data.second
                    )
                }
                else -> flowOf(it)
            }
        }.mapToModel()
}

The test itself:

@Test
fun `during creating, when data were saved into the remote source and failed to save it locally, then an error should be returned`() =
    runTest {
        // setup
        coEvery { locationRemoteSource.create(any(), any()) } returns flowOf(Result.Success(locationPair))
        coEvery { locationLocalSource.create(any(), any()) } returns flowOf(Result.Failure(mockk()))
        coEvery { locationRepositoryMapper.map(any()) } returns locationPair
        // invoke
        val result = repository.create(mockk())
        // verification
        coVerify(exactly = 1) { locationRemoteSource.create(any(), any()) }
        coVerify(exactly = 1) { locationLocalSource.create(any(), any()) }
        // assertion
        result.collect {
            assertTrue(it.isFailure)
        }
    }

The problem that it fails on line:

coVerify(exactly = 1) { locationLocalSource.create(any(), any()) }

It prints out that locationLocalSource is not called

Verification failed: call 1 of 1: LocationLocalSource(#3).create(any(), any(), any())) was not called

I don’t know why it happens since according to my understanding if remote source returns success result, then create fun from local source should be called. Where I'm wrong? thank you in advance.

P.S. verification of the remote source passes without problems

update #1: Definition of LocationLocalSource class in a test class

private val locationPair = Pair(mockk<LocationBase>(), mockk<LocationVersion>())
private val locationLocalSource = mockk<LocationLocalSource>()
private val locationRemoteSource = mockk<LocationRemoteSource>()
private val locationRepositoryMapper = mockk<RepositoryMapper<Location, LocationBase, LocationVersion>>()
private val repository = LocationRepositoryImpl(
    locationLocalSource = locationLocalSource,
    locationRemoteSource = locationRemoteSource,
    locationRepositoryMapper = locationRepositoryMapper
)
LeoColman
  • 6,950
  • 7
  • 34
  • 63
P. Savrov
  • 1,064
  • 4
  • 17
  • 29
  • how is `locationLocalSource ` defined in the test? how is the class under test initialized? – sidgate Jun 15 '20 at 08:08
  • @sidgate, question updated – P. Savrov Jun 15 '20 at 12:10
  • 1
    seems `LocationLocalSource ` create method has third parameter with some default value. try with `coEvery { locationLocalSource.create(any(), any(), any()) } returns flowOf(Result.Failure(mockk()))` – sidgate Jun 15 '20 at 12:25
  • @sidgate, create fun has only two params. Anyway, its something with a kotlin flow here. But thank you for a try :) – P. Savrov Jun 15 '20 at 14:24

1 Answers1

0

In short:

1.modify test fun like this:

// invoke
val result = runTest {
  repository.create(mockk())
}

2.modify runTest fun:

fun <T> runTest(block: suspend () -> Flow<T>): List<T> {
    val result = mutableListOf<T>()
    runBlockingTest {
        launch {
            block()
                .onStart {
                    println("${Instant.now()}: starts")
                }
                .onEach {
                    println("${Instant.now()}: $it")
                }
                .onCompletion { ex -> ex?.let {
                    println("${Instant.now()}: $it") }
                }
                .collect {
                    result.add(it)
                }
        }
    }
    return result
}
P. Savrov
  • 1,064
  • 4
  • 17
  • 29