一个修剪过的例子:
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会打勾的虚拟时间?
是的,看看这个有你正在寻找的行为的简单测试: https://github.com/ReactiveX/RxSwift/blob/b3f4bf1/Tests/RxSwiftTests/Tests/Observable+TimeTest.swift#L496要将TestScheduler交换为MainScheduler,我建议您将其作为依赖项注入.
另外,要检查_exerciseRemainingTime的值,您需要删除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.这样就不需要视图模型具有一个配置包并管理一次性使用的生命周期.
你应该考虑暴露比_exerciseRemainingTime更少“内部”的东西.也许类似于currentExercise:Observable< ExerciseEnum>,它在内部基于_exerciseRemainingTime.这样,您的视图控制器可以订阅并执行与视图控制器相关的简单工作,即可进行下一个练习.
另外,为了简化测试,您可以将20变量注入到视图模型中,这样在测试中您可以提供更小的东西,如3,然后正确只需要几个元素长.