Android单元测试:如何测试来自RxJava的retryWhen是否被称为某些数字? 我所拥有的

Android单元测试:如何测试来自RxJava的retryWhen是否被称为某些数字? 我所拥有的,android,unit-testing,junit,mockito,rx-java,Android,Unit Testing,Junit,Mockito,Rx Java,我有一个静态类APIHelper,并且有一个公共函数 /** * Calls APIs that return Observable<Response<*>>. * @param observable an Observable<Response<SomeModel>> you can get from NetworkServiceGenerator.getApi() * @param maxRetryCount retry count to

我有一个静态类
APIHelper
,并且有一个公共函数

/**
 * Calls APIs that return Observable<Response<*>>.
 * @param observable an Observable<Response<SomeModel>> you can get from NetworkServiceGenerator.getApi()
 * @param maxRetryCount retry count to try up to. Default is 3 (see DEFAULT_RETRY_COUNT)
 * @param customErrorToCatch if included, you can throw any Exception with custom logic
 * @param haltIfThrownType if included, you can halt the operation even before try count didn't reach retry count
 */
fun <T : Response<*>> call(
        observable: Observable<T>,
        maxRetryCount: Int = DEFAULT_MAX_RETRY_COUNT,
        retryIntervalInSec: Long = DEFAULT_RETRY_INTERVAL_SEC,
        customErrorToCatch: ((response: T) -> Unit)? = null,
        haltIfThrownType: ((throwable: Throwable?) -> Boolean)? = null): Observable<T> {
    return observable
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .doOnNext {
                if (!it.isSuccessful) {
                    if (customErrorToCatch != null) {
                        customErrorToCatch(it)
                    }
                    throwNetworkException(it)
                }
            }
            .retryWhen { errors ->
                errors.zipWith(
                        Observable.range(1, maxRetryCount),
                        BiFunction { throwable: Throwable, count: Int -> Pair(throwable, count) })
                        .flatMap { pair: Pair<Throwable, Int> ->
                            val throwable = pair.first
                            val currentCount = pair.second
                            val isNetworkException = pair.first is NetworkException
                            if (shouldStopRetrying(haltIfThrownType, throwable, currentCount, maxRetryCount)) {
                                Observable.error(throwable)
                            } else {
                                Observable.timer(retryIntervalInSec, TimeUnit.SECONDS)
                            }
                        }
            }
}
我的测试是

@Test
fun testRetriableError() {
    val response = Response.error<ResponseModel>(
            500,
            ResponseBody.create(
                    MediaType.parse("application/json"),
                    "{\"result\":\"fail\",\"errorCode\":\"4\",\"errorMessage\":\"\"}")
    )
    Mockito.`when`(api.requestLogout()).thenReturn(Observable.just(response))
    val testScheduler = TestScheduler(0, TimeUnit.SECONDS)
    val result = api.requestLogout().call(
            observeOn =  testScheduler
    )
    val testObserver = TestObserver<Response<ResponseModel>>()
    result.subscribe(testObserver)
    testScheduler.advanceTimeBy(1, TimeUnit.SECONDS)
    testObserver.assertNotTerminated()
    testScheduler.advanceTimeBy(1, TimeUnit.SECONDS)
    testObserver.assertNotTerminated()
    testScheduler.advanceTimeBy(1, TimeUnit.SECONDS)
    testObserver.assertNotTerminated()
    testScheduler.advanceTimeBy(1, TimeUnit.SECONDS)
    testObserver.assertTerminated() // <--- fails
    testObserver.assertNotComplete()
    testObserver.assertError(NetworkException(500, "Internal Server Error"))
    testObserver.assertValueCount(0)
}
但是,当我调试这个测试并逐行运行它们(花费足够的时间)时,测试通过了

我几乎觉得
testScheduler.advanceTimeBy(1,TimeUnit.SECONDS)
什么都不做


我做错了什么?

1。使用扩展函数,而不是传入可观察对象。2.使用默认参数传入subscribeOn/observeOn和timer的计划程序。然后,您可以传入TestScheduler并提前时间,以便测试在X次重试后发生的情况。如果您仍然有问题,请随时联系。我们将立即尝试并回复您。感谢您帮助@HansWurst@我编辑这个问题是为了在你的评论之后添加我尝试过的内容。我感觉我就快到了。你必须把一个调度程序传递到
Observable.timer(retryIntervalInSec,TimeUnit.SECONDS)
。否则,它将使用默认的调度程序。这里有一个重载,您可以传入一个调度程序,比如
#timer(时间、单位、调度程序)
。您可以只对时间作业使用
Schedulers.single()
,但请记住将其作为默认参数传入,以便在Testscheduler上提升时间,否则您会出现并发问题,以便将测试junit线程与传入的scheduler.1同步。使用扩展函数,而不是传入可观察对象。2.使用默认参数传入subscribeOn/observeOn和timer的计划程序。然后,您可以传入TestScheduler并提前时间,以便测试在X次重试后发生的情况。如果您仍然有问题,请随时联系。我们将立即尝试并回复您。感谢您帮助@HansWurst@我编辑这个问题是为了在你的评论之后添加我尝试过的内容。我感觉我就快到了。你必须把一个调度程序传递到
Observable.timer(retryIntervalInSec,TimeUnit.SECONDS)
。否则,它将使用默认的调度程序。这里有一个重载,您可以传入一个调度程序,比如
#timer(时间、单位、调度程序)
。您可以只对时间作业使用
Schedulers.single()
,但请记住将其作为默认参数传入,以便在Testscheduler上提升时间,否则您会遇到并发问题,以便将测试junit线程与传入的调度程序同步。
fun <T: Response<*>> Observable<T>.call(
        subscribeOn: Scheduler = Schedulers.io(),
        observeOn: Scheduler = AndroidSchedulers.mainThread(),
        maxRetryCount: Int = DEFAULT_MAX_RETRY_COUNT,
        retryIntervalInSec: Long = DEFAULT_RETRY_INTERVAL_SEC,
        customErrorToCatch: ((response: T) -> Unit)? = null,
        haltIfThrownType: ((throwable: Throwable?) -> Boolean)? = null): Observable<T> {
    return this.subscribeOn(subscribeOn)
            .observeOn(observeOn)
            .doOnNext {
                if (!it.isSuccessful) {
                    if (customErrorToCatch != null) {
                        customErrorToCatch(it)
                    }
                    throwNetworkException(it)
                }
            }
            .retryWhen { errors ->
                errors.zipWith(
                        Observable.range(1, maxRetryCount),
                        BiFunction { throwable: Throwable, count: Int -> Pair(throwable, count) })
                        .flatMap { pair: Pair<Throwable, Int> ->
                            val throwable = pair.first
                            val currentCount = pair.second
                            if (shouldStopRetrying(haltIfThrownType, throwable, currentCount, maxRetryCount)) {
                                Observable.error(throwable)
                            } else {
                                Observable.timer(retryIntervalInSec, TimeUnit.SECONDS)
                            }
                        }
            }
}
@Test
fun testRetriableError() {
    val response = Response.error<ResponseModel>(
            500,
            ResponseBody.create(
                    MediaType.parse("application/json"),
                    "{\"result\":\"fail\",\"errorCode\":\"4\",\"errorMessage\":\"\"}")
    )
    Mockito.`when`(api.requestLogout()).thenReturn(Observable.just(response))
    val testScheduler = TestScheduler(0, TimeUnit.SECONDS)
    val result = api.requestLogout().call(
            observeOn =  testScheduler
    )
    val testObserver = TestObserver<Response<ResponseModel>>()
    result.subscribe(testObserver)
    testScheduler.advanceTimeBy(1, TimeUnit.SECONDS)
    testObserver.assertNotTerminated()
    testScheduler.advanceTimeBy(1, TimeUnit.SECONDS)
    testObserver.assertNotTerminated()
    testScheduler.advanceTimeBy(1, TimeUnit.SECONDS)
    testObserver.assertNotTerminated()
    testScheduler.advanceTimeBy(1, TimeUnit.SECONDS)
    testObserver.assertTerminated() // <--- fails
    testObserver.assertNotComplete()
    testObserver.assertError(NetworkException(500, "Internal Server Error"))
    testObserver.assertValueCount(0)
}
java.lang.AssertionError: Subscriber still running! (latch = 1, values = 0, errors = 0, completions = 0)