Kotlin:如何将使用Thread.sleep的测试转换为RxJava TestScheduler

时间:2017-10-12 08:41:24

标签: multithreading kotlin rx-java integration-testing rx-java2

我正在编写一个工具测试,它会检查当我将某些内容缓存到Rx缓冲区时,并且在一段时间(10秒)之后,此主题将缓冲的值插入到我的Room数据库中。

当我使用Thread.sleep(syncTimeInterval)时,测试是正确的。我想使用TestScheduler编写相同的测试。

这是Thread.sleep版本(通过测试):

@Test
fun testMultipleLogs() {
    val loadAllCloudCallBefore = appDatabase.logCloudCallDao().loadAll()
    val loadAllLogNewSessionBefore = appDatabase.logNewSessionDao().loadAll()

    assertEquals(0, loadAllCloudCallBefore.size)
    assertEquals(0, loadAllLogNewSessionBefore.size)


    Observable.interval(1, TimeUnit.SECONDS)
            .take(20)
            .subscribe { logManager.logNewSession() }

    Observable.interval(1, TimeUnit.SECONDS)
            .take(20)
            .subscribe { logManager.logCloudCall("url", "callgoup") }

    Observable.interval(1, TimeUnit.SECONDS)
            .take(20)
            .subscribe { logManager.logNewSession() }

    Observable.interval(1, TimeUnit.SECONDS)
            .take(20)
            .subscribe { logManager.logCloudCall("url", "callgoup") }

    Observable.interval(1, TimeUnit.SECONDS)
            .take(20)
            .subscribe { logManager.logNewSession() }

    Observable.interval(1, TimeUnit.SECONDS)
            .take(20)
            .subscribe { logManager.logCloudCall("url", "callgoup") }


    Thread.sleep(30000)

    val loadAllCloudCallAfter = appDatabase.logCloudCallDao().loadAll()
    val loadAllLogNewSessionAfter = appDatabase.logNewSessionDao().loadAll()

    assertEquals(60, loadAllCloudCallAfter.size)
    assertEquals(60, loadAllLogNewSessionAfter.size)
}

在这里,这个测试没有通过,TestScheduler预计时间推进后的大小是0(不是60)

@Test
fun testMultipleLogs() {
    var testScheduler: TestScheduler = TestScheduler()

    val loadAllCloudCallBefore = appDatabase.logCloudCallDao().loadAll()
    val loadAllLogNewSessionBefore = appDatabase.logNewSessionDao().loadAll()

    assertEquals(0, loadAllCloudCallBefore.size)
    assertEquals(0, loadAllLogNewSessionBefore.size)


    Observable.interval(1, TimeUnit.SECONDS, testScheduler)
            .take(20)
            .subscribe { logManager.logNewSession() }

    Observable.interval(1, TimeUnit.SECONDS, testScheduler)
            .take(20)
            .subscribe { logManager.logCloudCall("url", "callgoup") }

    Observable.interval(1, TimeUnit.SECONDS, testScheduler)
            .take(20)
            .subscribe { logManager.logNewSession() }

    Observable.interval(1, TimeUnit.SECONDS, testScheduler)
            .take(20)
            .subscribe { logManager.logCloudCall("url", "callgoup") }

    Observable.interval(1, TimeUnit.SECONDS, testScheduler)
            .take(20)
            .subscribe { logManager.logNewSession() }

    Observable.interval(1, TimeUnit.SECONDS, testScheduler)
            .take(20)
            .subscribe { logManager.logCloudCall("url", "callgoup") }


    testScheduler.advanceTimeBy(21, TimeUnit.SECONDS)

    val loadAllCloudCallAfter = appDatabase.logCloudCallDao().loadAll()
    val loadAllLogNewSessionAfter = appDatabase.logNewSessionDao().loadAll()

    assertEquals(60, loadAllCloudCallAfter.size)
    assertEquals(60, loadAllLogNewSessionAfter.size)
}

如何正确测试此案例?有办法吗?

更新

LogManager中的函数如下所示:

  fun logCloudCall(url: String, callGroup: String) {
    val logCloudCall = LogCloudCall(url = url, callGroup = callGroup, date = Converter.GENERAL_DATE_FORMAT.format(Date()))

    Log.v("LogManager", logCloudCall.toString())
    addLog(logCloudCall)
}

   fun logNewSession() {
    val logNewSession =
            LogNewSession(
                    date = Converter.GENERAL_DATE_FORMAT.format(Date()))
    Log.v("LogManager", logNewSession.toString())

    addLog(logNewSession)
}

   fun addLog(logEvent: LogEvent) {
    source.onNext(logEvent)
}

这是我在LogManager init中使用的机制:

 val source = PublishSubject.create<LogEvent>().toSerialized()

var logRepository: LogRepository

init {
    logRepository = LogRepositoryImpl(context)
    configureSubject()
}


fun configureSubject() {
    source
            .buffer(10, TimeUnit.SECONDS)
            .subscribe { bufferedData -> proceedValues(bufferedData) }
}

1 个答案:

答案 0 :(得分:0)

The following test passes:

@Test
fun foo() {
    val testScheduler = TestScheduler()
    var count = 0

    Observable.interval(1, TimeUnit.SECONDS, testScheduler)
          .take(20)
          .subscribe { count++ }

    testScheduler.advanceTimeBy(21, SECONDS)

    assert(count == 20)
}

That is, your test code looks correct, but the result is not correct. The only unknown here is the logManager code. Is there any threading in that class? That may explain why the count is still 0: you may have a race condition.


It's probably due to the buffer call. buffer internally uses the a computation Scheduler:

public final Observable<List<T>> buffer(long timespan, TimeUnit unit) {
    return buffer(timespan, unit, Schedulers.computation(), Integer.MAX_VALUE);
}

This will probably result in the threading issue you're seeing.