如何测试 RxSwift Observable.interval 进度
How to test RxSwift Observable.interval progress
我有一个视图模型,其行为由 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
滴答作响的虚拟时间?
是的,看看这个具有您正在寻找的行为的简单测试:https://github.com/ReactiveX/RxSwift/blob/b3f4bf1/Tests/RxSwiftTests/Tests/Observable+TimeTest.swift#L496
要将 TestScheduler
替换为 MainScheduler
,我建议您将其作为依赖项注入。
此外,要检查 _exerciseRemainingTime
的值,您需要删除 private
。但是,我不建议测试 class 的内部结构。删除 private
表示您是。相反,如果您要注入一个负责执行 progressToNextExercise
的对象,那么您可以测试它是否收到了进行下一个练习的调用。您只需在测试期间传递该对象的测试版本,就像您为调度程序使用 TestScheduler
一样。这样就不需要制作 _exerciseRemainingTime
public 来测试它,甚至不需要知道它。
但是,为了这个问题的主要目的,忽略 _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
s 的生命周期。
您真的应该考虑公开 "internal" 少于 _exerciseRemainingTime
的内容。可能类似于 currentExercise: Observable<ExerciseEnum>
,它在内部基于 _exerciseRemainingTime
。这样,您的视图控制器就可以订阅并执行简单的视图控制器相关工作,继续下一个练习。
此外,为了简化测试,您可以将 20
变量注入视图模型,这样在测试中您可以提供更小的变量,例如 3
然后 correct
将只需要几个元素长。
我有一个视图模型,其行为由 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
滴答作响的虚拟时间?
是的,看看这个具有您正在寻找的行为的简单测试:https://github.com/ReactiveX/RxSwift/blob/b3f4bf1/Tests/RxSwiftTests/Tests/Observable+TimeTest.swift#L496
要将 TestScheduler
替换为 MainScheduler
,我建议您将其作为依赖项注入。
此外,要检查 _exerciseRemainingTime
的值,您需要删除 private
。但是,我不建议测试 class 的内部结构。删除 private
表示您是。相反,如果您要注入一个负责执行 progressToNextExercise
的对象,那么您可以测试它是否收到了进行下一个练习的调用。您只需在测试期间传递该对象的测试版本,就像您为调度程序使用 TestScheduler
一样。这样就不需要制作 _exerciseRemainingTime
public 来测试它,甚至不需要知道它。
但是,为了这个问题的主要目的,忽略 _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
s 的生命周期。
您真的应该考虑公开 "internal" 少于 _exerciseRemainingTime
的内容。可能类似于 currentExercise: Observable<ExerciseEnum>
,它在内部基于 _exerciseRemainingTime
。这样,您的视图控制器就可以订阅并执行简单的视图控制器相关工作,继续下一个练习。
此外,为了简化测试,您可以将 20
变量注入视图模型,这样在测试中您可以提供更小的变量,例如 3
然后 correct
将只需要几个元素长。