欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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: 首先新建一个工程,并勾选包括单元测试,如下:

RxSwift - Test operators

2: 使用Cocoapods导入相应的库,如果不会使用,可以看我之前的文章,podfile文件如下:

RxSwift - Test operators

3:打开.xcworkspace项目,command+B编译无错,然后开始定位TestDemoTests.swift文件

RxSwift - Test operators

RxSwift - Test operators

4: 导入RxSwiftRxTest,暂时去除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:判断结果与预期是否相符合,运行正确

参考: