K.Os
K.Os

Reputation: 5496

How to mock reactive repository which returns Observable

So i have repository, which provides the Observable to the client. Is there a way i can mock this repository, so i don't need to send location from my emulator or using the real device to gain some Location?

Here is how the interface looks like:

interface RxLocationRepository {

@SuppressLint("MissingPermission")
fun onLocationUpdate(): Observable<Location>

fun stopLocationUpdates()
}

In my client side i use this like this:

class LocationManager(
    val rxLocationRepository: RxLocationRepository){

private fun appendGeoEvent(location: Location) {
    val locationGeoEvent = LocationGeoEvent(
            accuracy = location.accuracy.toDouble(),
            latitude = location.latitude,
            longitude = location.longitude,
            timestampGeoEvent = location.time
    )
    processGeoEvent(locationGeoEvent)
}

compositeDisposable.add(rxLocationRepository.onLocationUpdate()
            .subscribe(Consumer { location ->
                appendGeoEvent(location)
            }))
 ....

So i sending this obtained location to my appendGeoEvent method.

I can use for example Mockito, but i don't know how to mock this repository so i can use the fake locations. Also, i want to use Kotlin.

Upvotes: 1

Views: 3167

Answers (2)

K.Os
K.Os

Reputation: 5496

I needed to setup different Dagger module, which just provide me implementation of this repository - it returns different Observable stream.

I set it up like this.

@Module
abstract class StubLocationRepositoryModule {

@Binds
internal abstract fun bindsRxLocationRepository(stubRxLocationRepository: StubRxLocationRepository) : RxLocationRepository
}

I was just using this in my androidTest package in component.

So my implementation was like this:

just an example:

class StubRxLocationRepository @Inject constructor(val stubLocationParameterName: String) : RxLocationRepository {

val location = Location("test").apply {
    latitude = 1.234
    longitude = 5.678
    accuracy = 20f
    time = Date().time
}

override fun onLocationUpdate(): Observable<Location> {
    Log.v("StubLocationRepository", "CHOSEN PARAMETER: $stubLocationParameterName")


    return when (stubLocationParameterName) {
        "highFreq" -> Observable.interval(50, TimeUnit.MILLISECONDS)
                .flatMap(
                        {
                            Observable.just(location)
                        }
                )
                .doOnNext{ t: Location -> Log.v("onLocationUpdate", t.toString()) }
        else -> Observable.interval(1, TimeUnit.SECONDS)
                .flatMap(
                        {
                            Observable.just(location)
                        }
                )
                .doOnNext{ t: Location -> Log.v("onLocationUpdate", t.toString()) }
    }
}

override fun stopLocationUpdates() {
    Log.v("StubLocationRepository", "stopLocationUpdates")
}
}

So in my Dagger Component builder i expose a method which will provide me some parameter in which i will depend in the StubRxLocation implementation - it will return me some stream i need for specific test case.

So the component:

@Singleton
@Component(modules = arrayOf(StubLocationRepositoryModule::class, 
LocationTestInstrumentalModule::class, StubRssiRepositoryModule::class))
interface LocationTestInstrumentalComponent{
fun locationClient(): LocationClient


@Component.Builder
interface Builder {
    @BindsInstance
    fun context(context: Context): Builder
    @BindsInstance
    fun stubLocationRepositoryParameter(stubLocationRepositoryParameter: String): Builder
    fun build(): LocationTestInstrumentalComponent
}

}

So in every test i can bring the mocked repository it like this, which will be ready form me to use for that test case:

@Test
fun someTest(){
       val component = DaggerLocationTestInstrumentalComponent.builder().stubLocationRepositoryParameter("highFreq").context(InstrumentationRegistry.getContext()).build()

val client = component.locationClient()
//i can expose some other methods, not only this 'locationClient' in this Component to return me some classes, like this RxLocationRepository(which will behave as i want) and others
}

Upvotes: 0

ESala
ESala

Reputation: 7058

If using Mockito, you could do something like this:

import android.location.Location
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito
import org.mockito.Mockito.`when`
import org.mockito.Mockito.mock
import org.mockito.junit.MockitoJUnitRunner
import java.util.*

@RunWith(MockitoJUnitRunner::class)
class LocationsTest{

    @Test
    fun should_return_one_location() {
        val location = Location("test").apply {
            latitude = 1.234
            longitude = 5.678
            // ...
        }
        val mockRepository = mock(RxLocationRepository::class.java)
        `when`(mockRepository.onLocationUpdate()).thenReturn(Observable.just(location))
        // use the mock
    }

}

I am using: testCompile "org.mockito:mockito-core:2.11.0"

Upvotes: 2

Related Questions