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

时间:2017-10-25 14:36:48

标签: unit-testing mockito kotlin rx-java rx-java2

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

以下是界面的外观:

loops

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

interface RxLocationRepository {

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

fun stopLocationUpdates()
}

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

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

2 个答案:

答案 0 :(得分:1)

如果使用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"

答案 1 :(得分:0)

我需要设置不同的Dagger模块,它只是为我提供了此存储库的实现 - 它返回不同的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实现 - 它将返回一些特定测试用例所需的流。

所以组件:

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