Ios 如何测试RxSwift可观测间隔进度

Ios 如何测试RxSwift可观测间隔进度,ios,swift,reactive-programming,rx-swift,Ios,Swift,Reactive Programming,Rx Swift,我有一个视图模型,它的行为由一个可观察的.interval控制。本质上,它会在每个下一个上更新一个计时器标签,并在一段时间后更新另一个值 例如: class WorkoutViewModel { private var _oneSecondTimer: Observable<Int> { return Observable<Int>.interval(1, scheduler: MainScheduler.instance) } p

我有一个视图模型,它的行为由一个
可观察的.interval
控制。本质上,它会在每个
下一个
上更新一个计时器标签,并在一段时间后更新另一个值

例如:

class WorkoutViewModel {
    private var _oneSecondTimer: Observable<Int> {
        return Observable<Int>.interval(1, scheduler: MainScheduler.instance)
    }
    private let _exerciseRemainingTime: Variable<Int> = Variable(20)

    func setupBehaviour() {
        _oneSecondTimer.subscribeNext() { [unowned self] _ in
            self._exerciseRemainingTime.value -= 1
            if self._exerciseRemainingTime.value == 0 {
                self.progressToNextExercise()
            }
        }
    }
}
class WorkoutViewModel{
私有变量oneSecondTimer:可观察{
返回可观察的.interval(1,调度程序:MainScheduler.instance)
}
私有let_exerciseRemainingTime:变量=变量(20)
函数设置行为(){
_oneSecondTimer.subscribeNext(){[unowned self]\uIn
self.\u exerciseRemainingTime.value-=1
如果self.\u exerciseRemainingTime.value==0{
self.progressToNextExercise()
}
}
}
}
我想对此进行测试,观察事件发生的时间和
\u exerciseRemainingTime
的值


有没有一种方法可以使用
TestScheduler
来模拟
\u oneSecondTimer
将在其中计时的虚拟时间?

是的,请查看这个简单的测试,它具有您想要的行为:

要将
TestScheduler
交换为
MainScheduler
,我建议您将其作为依赖项注入

此外,要检查
\u exerciseRemainingTime
的值,您需要删除
private
。然而,我不建议测试类的内部结构。删除
private
表示您已被删除。相反,如果您要注入一个负责执行
progressToNextExercise
的对象,那么您可以测试它是否收到了一个调用,以进行下一个练习。您只需在测试期间传入该对象的测试版本,就像您使用调度程序的
TestScheduler
一样。这样就不需要公开
\u exerciseRemainingTime
来测试它,甚至不需要知道它

但是,出于这个问题的主要目的,忽略了
\u exerciseRemainingTime
的可见性,以下是我对调度程序的理解:

WorkoutViewModel.swift
需要考虑的几个注意事项:

为了让测试调度器订阅和处理,我从视图模型中删除了
subscribebenext
。无论如何,我认为这会改进它,因为您应该订阅视图控制器,并且只使用视图模型为您提供
可观察的
。这样,视图模型就不需要有一个处置袋,也不需要管理
一次性
的生命周期

你确实应该考虑把一些“内部”比“代码”>“锻炼”的时间暴露出来。可能类似于

currentExercise:Observable
,它内部基于
\u exerciseRemainingTime
。这样,视图控制器就可以订阅并执行与视图控制器相关的简单工作,即进入下一个练习


此外,为了简化测试,您可以将
20
变量注入视图模型,以便在测试中可以提供较小的变量,如
3
,然后
正确的
只需要几个元素就可以了。

谢谢您的回答。使用注入式调度程序是我一直在寻找的。至于
private\u exerciseRemainingTime
,我想尽量减少问题中的类示例,省略很多(可能太多),只关注手头的问题。谢谢最后的建议,这就是我在全班的表现。
class WorkoutViewModel {
    private var _oneSecondTimer: Observable<Int> {
        return Observable<Int>.interval(1, scheduler: scheduler)
    }

    // not `private` anymore.  also, a computed property
    var _exerciseRemainingTime: Observable<Int> {
        return self._oneSecondTimer.map { i in
            20 - i
        }
    }

    // injected via `init`
    private let scheduler: SchedulerType

    init(scheduler: SchedulerType) {
        self.scheduler = scheduler
    }
}
func testExerciseRemainingTime() {
    let scheduler = TestScheduler(initialClock: 0)

    let res = scheduler.start(0, subscribed: 0, disposed: 23) {
        WorkoutViewModel(scheduler: scheduler)._exerciseRemainingTime
    }

    let correct = [
        next(1, 20), // output .Next(20) at 1 second mark
        next(2, 19), // output .Next(19) at 2 second mark
        next(3, 18),
        next(4, 17),
        next(5, 16),
        next(6, 15),
        next(7, 14),
        next(8, 13),
        next(9, 12),
        next(10, 11),
        next(11, 10),
        next(12, 9),
        next(13, 8),
        next(14, 7),
        next(15, 6),
        next(16, 5),
        next(17, 4),
        next(18, 3),
        next(19, 2),
        next(20, 1),
        next(21, 0),
        next(22, -1),
    ]

    XCTAssertEqual(res.events, correct)
}