RxSwift - Test operators
程序员文章站
2022-07-14 22:30:06
...
使用RxTest测试operators
RxTest是一个独立于RxSwift的库。所以使用需要单独倒入,可以使用pod进行安装,RxTest提供了许多有用的功能用于测试RxSwift的代码。比如:TestScheduler,它是一个虚拟的时间调度者(virtual time scheduler),能够让我们很好的控制线性时间操作符的测试( testing time-linear operations),而且包括next(_:_:), completed(_:_:), and error(_:_:)等方法,能够让我们在测试的具体时间添加事件到观察者序列(observables)。它也可以添加hot和cold观察者序列,跟RxSwift功能类似。
Hot and Cold Observables
Hot observables:
• Use resources whether there are subscribers or not.
• Produce elements whether there are subscribers or not.
• Are primarily used with stateful types such as Variable.
Cold observables:
• Only consume resources upon subscription.
• Only produce elements if there are subscribers.
• Are primarily used for async operations such as networking.
基本概念
TestScheduler:大多数时候,我们将使用TestScheduler的两个方法,一个是createObserver,一个是createHotObservable。
createObserver: 允许你创建TestableObserver,它将记录每次发送给它的事件。
createHotObservable:创建hot observable,这里我们可以传递事件给它,在指定的调度者(scheduler)中。
initialClock:该方法用于创建一个新的调度者。调度者(Schedulers)用于派发工作到多个线程。调度者是观察者序(Observables)实现异步事件的核心。但是测试异步代码并不容易。TestScheduler派发它的工作到主线程(main thread)并且使用虚拟时间(virtual time)来记录当事件发生的时刻,在大多数情况下,我们将设置initialClock为0.
TestableObserver & TestableObservable
当我们创建了测试调度者,我们就可以进一步工作了,根据调度者创建TestableObserver 和 TestableObservable。
Tests & the Driver unit
测试Driver可能比较棘手。因为Driver经常切换线程到主调度者(MainScheduler)进行工作。所以有时候,testScheduler并不能够捕获到事件,而且测试不能继续,但是RxCocoa为我们提供了一个很好的函数来处理这个问题, func driveOnScheduler(_ scheduler: SchedulerType, action: () -> ()),能够改变Driver的scheduler。
简单Demo
1: 首先新建一个工程,并勾选包括单元测试,如下:
2: 使用Cocoapods导入相应的库,如果不会使用,可以看我之前的文章,podfile文件如下:
3:打开.xcworkspace项目,command+B编译无错,然后开始定位TestDemoTests.swift文件
4: 导入RxSwift和RxTest,暂时去除testExample()和testPerformanceExample()两个方法,添加两个实例
import XCTest
import RxSwift
import RxTest
@testable import TestDemo
class TestDemoTests: XCTestCase {
var scheduler: TestScheduler! //scheduler是TestScheduler的实例,将被用于每一次测试
var subscription: Disposable! //subscription将持有你每次测试的订阅
override func setUp() {
super.setUp()
//1
scheduler = TestScheduler(initialClock: 0)
}
override func tearDown() {
//2
scheduler.scheduleAt(1000) {
self.subscription.dispose()
}
super.tearDown()
}
}
注意:setUp()方法在每次开始测试之前会被调用,这是单元测试的基本内容,想了解unit-test,可以看这里。
1:使用TestScheduler的初始化方法创建scheduler实例,指定initialClock为0,这表示你想在测试的开始时测试scheduler
2:tearDown在每次测试完成后被调用。在该方法里面,你的调度者(scheduler)将处理测试订阅者在1000毫秒的时候。注意:我们所写的测试每次运行都会少于一秒,所以这里在1秒的时候处理测试的订阅者(test’s subscription)的安全的。
5: 现在开始,写几个简单例子。测试amb操作符。我们在组合操作符中学习了amb,对于两个或者多个观察者序列,仅仅只有最先发送消息的观察者序列才能被订阅者监听。并且之后只有该序列发送消息有效。
//1:
func testAmb() {
//2:
let observer = scheduler.createObserver(String.self)
//3:
let observableA = scheduler.createHotObservable([
//4:
next(100,"a)"),
next(200,"b)"),
next(300,"c)")
])
//5:
let observableB = scheduler.createHotObservable([
next(90,"1)"),
next(200,"2)"),
next(300,"3)")
])
//6:
let ambObservable = observableA.amb(observableB)
//7:
scheduler.scheduleAt(0) {
//8:
self.subscription = ambObservable.subscribe(observer)
}
//9:
scheduler.start()
//10:
let results = observer.events.map {
$0.value.element!
}
//11:
XCTAssertEqual(results, ["1)","2)","3)"]) //ok
}
分析:
1:跟使用XCTest一样,方法命令必须使用test开始,这里我们创建了一个测试函数,命令为testAmb
2:使用scheduler的createObserver方法创建一个观察者,指定类型为String.观察者将记录每一次接受事件的时间戳,除了不打印任何内容,其它像RxSwift中的操作符debug
3:创建一个可测试的观察者序列observableA
4: 使用next(_:_:)方法在指定的时间添加.next事件到observableA,第一个参数为添加时间,第二个参数为具体值。即在具体的时间发送该事件。对于需要发送值的就指定值,如果是向按钮点击是不需要发送值的,那么就为空,使用小圆括号()即可
5:创建另外一个可测试的观察者序列observableB,同样在指定时间添加.next事件到observableB
6:接下来使用amb操作符,ambObservable的类型时Observable<String>
7:接下来告诉调度者(scheduler)安排具体时间的动作
8:为观察者序列添加观察者,并且是在时间为0的时候开始订阅。返回的订阅者进行引用,这样在tearDown中进行释放处理
9:为了开始测试操作来验证结果,我们需要使用start方法,这里开始了虚拟时间的调度者,观察者(observer)将接受.next事件,事件是最初通过amb操作符之后得到的ambObservable中的时间和元素
10:现在可以收集和分析结果数据,这里使用map操作符来转换观察者事件到对应事件到元素值
11:现在,我们可以使用断言来比较实际的数据和预期的结果数据,测试结果是正确的,因为observableB中的事件将被发送,所以与预期结果一致。
6:测试filter操作符
func testFilter() {
//1
let observer = scheduler.createObserver(Int.self)
//2
let observable = scheduler.createHotObservable([
next(100, 1),
next(200, 2),
next(300, 3),
next(400, 2),
next(500, 1)
])
//3
let filterObservable = observable.filter {
$0 < 3
}
//4
scheduler.scheduleAt(0) {
self.subscription = filterObservable.subscribe(observer)
}
//5
scheduler.start()
//6
let results = observer.events.map {
$0.value.element!
}
//7
XCTAssertEqual(results, [1,2,2,1])
}
分析:
1:创建一个观察者,指定观察者序列事件的元素类型为Int
2:创建一个观察者序列,并且添加元素,使用next方法
3:过滤原观察者序列得到过滤之后的观察者序列
4:在0秒时,开始订阅观察者序列,并且使用全局变量subscription引用
5:开始执行调度者(scheduler)
6:收集结果
7:判断结果与预期是否相符合,运行正确
参考:
推荐阅读
-
详解在vue-test-utils中mock全局对象
-
java.lang.NoSuchMethodError: cn.makangning.test.dao.Users.getUserBirthday()Ljava/sql/Date;
-
javascript 使用正则test( )第一次是 true,第二次是false
-
Win10 AV-Test的杀软大PK:Win10 Defender不再花瓶
-
There are no operators in the program to be executed 可能的原因
-
MySQL test数据库的权限
-
SQLite3源码学习之test_vfs的共享内存机制讲解
-
VBS教程:方法-Test 方法
-
win8杀毒软件哪家强?AV-Test公布Win 8/8.1最佳防毒软件
-
js 正则表达式之test函数讲解