如何测试RxSwift Observable.interval进度

时间:2016-07-21 07:36:57

标签: ios swift reactive-programming rx-swift

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

一个修剪过的例子:

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

我想对此进行测试,以观察事件的时间和_exerciseRemainingTime的值。

有没有办法如何使用TestScheduler来模拟_oneSecondTimer会勾选的虚拟时间?

1 个答案:

答案 0 :(得分:7)

是的,请查看具有您正在寻找的行为的简单测试:https://github.com/ReactiveX/RxSwift/blob/b3f4bf1/Tests/RxSwiftTests/Tests/Observable+TimeTest.swift#L496

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

另外,要检查_exerciseRemainingTime的值,您需要删除private。但是,我不建议测试你班级的内部。删除private就是您的标志。相反,如果您要注入一个负责progressToNextExercise的对象,那么您可以测试它是否接到了进行下一个练习的调用。您只需在测试期间传入该对象的测试版本,就像调度程序使用TestScheduler一样。这样就无需公开_exerciseRemainingTime来测试它,甚至不知道它。

但是,为了这个问题的主要目的,忽略了_exerciseRemainingTime的可见性,这就是我对调度程序的意思:

WorkoutViewModel.swift

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

WorkoutViewModelTest.swift

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

要考虑几个注意事项:

为了让测试调度程序订阅和处置,我从视图模型中删除了subscribeNext。我认为这无论如何都会改进它,因为您应该使用视图控制器订阅并仅使用视图模型为您提供Observable。这样就不需要视图模型具有一个配置包并管理Disposable的生命周期。

你应该考虑更少地暴露内容&#34;内部&#34;比_exerciseRemainingTime。也许像currentExercise: Observable<ExerciseEnum>这样的内容基于_exerciseRemainingTime。这样,您的视图控制器可以订阅并执行与视图控制器相关的简单工作,即可进行下一个练习。

另外,为了简化测试,您可以将20变量注入视图模型,这样在测试中您可以提供像3那样较小的内容,然后correct只需要是一些元素。