单元测试
测试自定义操作符
RxSwift用RxTest为所有操作符测试,位于Rx.xcworkspace项目内的AllTests-*目标中。
这是典型的RxSwift操作符单元测试的示例:
func testMap_Range() {
// 初始化测试调度程序。.
// 测试调度程序实现虚拟时间与本地机器时钟分离。
// 这样可以尽快运行模拟并证明所有事件都已处理完毕。
let scheduler = TestScheduler(initialClock: 0)
// 创建一个模拟热的可观察序列。
// 序列将在指定的时间发出事件,无论是否有观察者订阅。
// 这就是热的意思)。
// 此可观察序列还将记录所有在其生命周期内的订阅(`subscriptions`属性)。
let xs = scheduler.createHotObservable([
.next(150, 1), // first argument is virtual time, second argument is element value
.next(210, 0),
.next(220, 1),
.next(230, 2),
.next(240, 4),
.completed(300) // virtual time when completed is sent
])
// 默认情况下,``start`方法:
// *运行模拟并记录所有事件
// 使用`res`引用的观察者。
// *在虚拟时间200订阅
// *在虚拟时间1000处理订阅
let res = scheduler.start { xs.map { $0 * 2 } }
let correctMessages = Recorded.events(
.next(210, 0 * 2),
.next(220, 1 * 2),
.next(230, 2 * 2),
.next(240, 4 * 2),
.completed(300)
)
let correctSubscriptions = [
Subscription(200, 300)
]
XCTAssertEqual(res.events, correctMessages)
XCTAssertEqual(xs.subscriptions, correctSubscriptions)
}
对于非终止序列,不一定关心事件时间,也可以使用RxTest的XCTAssertRecordedElements来断言已发出的特定元素。终止事件(例如completed或error)将导致测试失败。
func testElementsEmitted() {
let scheduler = TestScheduler(initialClock: 0)
let xs = scheduler.createHotObservable([
.next(210, "RxSwift"),
.next(220, "is"),
.next(230, "pretty"),
.next(240, "awesome")
])
let res = scheduler.start { xs.asObservable() }
XCTAssertRecordedElements(res.events, ["RxSwift", "is", "pretty", "awesome"])
}
测试操作符组合(视图模型,组件)
如何测试组合操作符的示例包含在Rx.xcworkspace> RxExample-iOSTeststarget中。
定义RxTest扩展很容易,因此可以以可读的方式编写测试。内部提供的示例RxExample-iOSTests只是关于如何编写这些扩展的建议,但是如何编写这些测试有很多可能性。
// 预期事件和测试数据
let (
usernameEvents,
passwordEvents,
repeatedPasswordEvents,
loginTapEvents,
expectedValidatedUsernameEvents,
expectedSignupEnabledEvents
) = (
scheduler.parseEventsAndTimes("e---u1----u2-----u3-----------------", values: stringValues).first!,
scheduler.parseEventsAndTimes("e----------------------p1-----------", values: stringValues).first!,
scheduler.parseEventsAndTimes("e---------------------------p2---p1-", values: stringValues).first!,
scheduler.parseEventsAndTimes("------------------------------------", values: events).first!,
scheduler.parseEventsAndTimes("e---v--f--v--f---v--o----------------", values: validations).first!,
scheduler.parseEventsAndTimes("f--------------------------------t---", values: booleans).first!
)
集成测试
也可以使用RxBlocking运算符编写集成测试。
使用RxBlocking的toBlocking()方法,可以阻止当前线程并等待序列完成,允许同步访问它的结果。
测试序列结果的一种简单方法是使用toArray方法。一旦序列成功完成,它将返回所有元素的数组,或者throw
如果错误导致序列终止。
let result = try fetchResource(location)
.toBlocking()
.toArray()
XCTAssertEqual(result, expectedResult)
另一种选择是使用materialize运算符,它可以更细致地检查序列。如果序列成功完成将返回MaterializedSequenceResult
枚举包含.completed
枚举和发出的元素,或者failed
如果序列以错误终止包含发射的错误。
let result = try fetchResource(location)
.toBlocking()
.materialize()
// 用于在终止错误的情况下测试结果或错误
switch result {
case .completed:
XCTFail("Expected result to complete with error, but result was successful.")
case .failed(let elements, let error):
XCTAssertEqual(elements, expectedResult)
XCTAssertErrorEqual(error, expectedError)
}
// 用于在完成终止的情况下测试结果
switch result {
case .completed(let elements):
XCTAssertEqual(elements, expectedResult)
case .failed(_, let error):
XCTFail("Expected result to complete without error, but received \(error).")
}