如何模拟 returns Observable 的反应式存储库

How to mock reactive repository which returns Observable

所以我有存储库,它向客户端提供 Observable。有没有一种方法可以模拟这个存储库,这样我就不需要从我的模拟器发送位置或使用真实设备来获得一些位置?

界面如下所示:

interface RxLocationRepository {

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

fun stopLocationUpdates()
}

在我的客户端,我是这样使用的:

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

所以我将这个获得的位置发送到我的 appendGeoEvent 方法。

我可以使用例如 Mockito,但我不知道如何模拟此存储库以便我可以使用假位置。 另外,我想使用 Kotlin。

如果使用 Mockito,你可以这样做:

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
    }

}

我正在使用:testCompile "org.mockito:mockito-core:2.11.0"

我需要设置不同的 Dagger 模块,它只为我提供了这个存储库的实现——它 return 是不同的 Observable 流。

我是这样设置的

@Module
abstract class StubLocationRepositoryModule {

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

我只是在组件的 androidTest 包中使用它。

所以我的实现是这样的:

举个例子:

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")
}
}

所以在我的 Dagger 组件构建器中,我公开了一个方法,该方法将为我提供一些我将在 StubRxLocation 实现中依赖的参数 - 它会 return 我需要一些特定测试用例的流。

所以组件:

@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
}

}

所以在每次测试中我都可以像这样带来模拟存储库,这将准备好供我用于该测试用例:

@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
}