RxJS系列06:测试 Observable

简介: RxJS系列06:测试 Observable

RxJS(Reactive Extensions for JavaScript) 是一个非常强大的 JS 库,我们可以使用它轻松编写异步代码。

在本系列文章中,我将带领你学习 RxJS 的最新版本,我们会重点关注如何使用响应式编程范式来解决你在日常工作中碰到的问题。所以这是一个偏实战的系列文章。

在本系列文章中,你将学会 RxJS 中的核心组件是如何使用和运作的。

通过学习这个系列文章,你将亲自使用 RxJS 完成一个完整的项目开发,在这个项目中,你将了解如何处理 DOM 事件、如何构建响应式本地数据库等内容。


RxJS 中测试思路


响应式编程范式与每一个软件开发范式一样,都需要使用测试来维护程序的正确性和可扩展性。

在 RxJS 中,我们最常见的场景就是订阅者订阅一个 Observable 并接收一些值,在这种场景下,我们只需要在对订阅者预期收到的值和实际收到的值进行比对就可以知道程序是否正确。

我们可以按照这种思路开始测试。


传统的测试方法


在正式开始测试之前,你需要对 Mocha.js 有一个基本的了解,因为这是我们接下来要用到的测试库。但是我不会在这里进行详细介绍。

我们首先来看下面这段代码:


const observable$ = getObservableForTest()
it('测试惰性 Observable', () => {
  observable$.subscribe(callbackFn)
})

在这段代码中,我们订阅了需要在测试回调中测试的 Observable。但是测试是同步执行的,这就意味着它会在订阅 Observable 后立即完成。我们希望测试程序在 Observable 发出值,并且验证是否与预期像等候再结束运行。

现在我们要使用 Mocha 中的 done 来手动告知测试程序应该在什么时候完成。


const observable$ = getObservableForTest()
it('测试惰性 Observable', (done) => {
  observable$.subscribe(val => {
    done()
  })
})

这样就满足了我们的需求,但是需要注意一点:这种方式是存在缺点的,我们必须记住要调用 done 方法。

现在我们继续添加一些测试逻辑来完成单元测试,我们测试实际发出的值是否为 1。


const observable$ = getObservableForTest()
it('测试惰性 Observable', (done) => {
  observable$.subscribe(val => {
    expect(val).toBe(1)
    done()
  })
})

到这里,测试单个值的单元测试就完成了。

但是我们该怎么样来进行多个值的测试呢?

这是这种测试方法的第二个缺点:测试发送多个值的 Observable 很困难,我们要编写很复杂的测试逻辑。

一种解决方法是使用 toArray 操作符,这个操作符可以等待 Observable 完成,然后将所有的值以数组的形式一次性发送给订阅者。我们可以使用这种方式编写测试代码:


const observable$ = getObservableForTest()
describe('异步', function () {
    it('转换成 Array', function (done) {
      from([1, 2, 3]).pipe(toArray()).subscribe(val => {
        assert.deepEqual(val, [1, 2, 3])
        done()
      })
    });
});

我们在这个测试中注意到一个问题,在响应式编程中,时间是必不可少的关键因素,因为数据是随着时间推移而产生的,无论是定期的还是不定期的。如果我们使用这种方法进行测试,我们无法知道第一次发出值的时间以及第二次、第三次发出的时间。

还有,我们没有办法使用这种测试方法在数据流中对数据进行转换。

所以,传统的测试方法并不适合在 RxJS 中进行测试。Marble 是一种更好的测试方案,它可以解决我在上面提到的所有缺点。


Marble


marble 可以使用虚拟时间来同步测试异步的 Observable。RxJS 中的虚拟时间是通过 Scheduler 提供的。


Scheduler


Scheduler 是一种数据结构,可以将不同的异步任务排队并根据标准执行它们,标准指的是优先级或者不同的执行算法。它提供了虚拟时钟 now 方法。Scheduler 调度的每个任务都是相对于虚拟时钟执行的。Scheduler 也分为多种类型,例如 AsyncScheduler、QueueScheduler、AsapScheduler 等。

我们可以使用 TestScheduler 来测试 Observable,因为它是内置的,不需要额外安装依赖。


Marble test


我们在学习操作符的时候,展示过一些示例图,这种图就是 Marble 图。这种图可以用来当作编写测试的参考。

下面是一个 Marble 图,它表示一些小写的字符是如何随着时间推移被映射为大写字符的。

image.png

下面是根据 Marble 图实现的同步代码。


import { from } from 'rxjs'
import { map }  from 'rxjs/operators'
const lowercaseCharsObservable$ = from(['a', 'b', 'c'])
lowercaseCharsObservable$
    .pipe(map(character => character.toUpperCase()))
    .subscribe(console.log)

对应的,还有异步代码。


import {Subject} from 'rxjs'
import { map } from 'rxjs/operators'
const lowercaseCharsObservable$ = new Subject()
lowercaseCharsObservable$
    .pipe(map(character => character.toUpperCase()))
    .subscribe(console.log)
setTimeout(() => {lowercaseCharsObservable$.next('a')}, 500)
setTimeout(() => {lowercaseCharsObservable$.next('b')}, 1000)
setTimeout(() => {lowercaseCharsObservable$.next('c')}, 2000)

很明显,异步的实现更难以测试,但是 Marble 可以让测试它们变得非常简单。

下面我们学习一下 Marble 语法,然后将这个用例作为代表,使用 Marble 进行测试。


Marble 语法


我们可以通过 Marble 语法创建操作序列,用声明的方式允许 Observable 的预期行为。

下面是一些 Marble 符号。

符号 意义
^ 表示 Observer 什么时候订阅了 Observable
| 表示 Observable 完成
(空字符) 不需要解释的独特字符
- 表示一帧的虚拟时间
a-z 表示 Observable 发出的值
() 将任何类型的时间分组在同一帧中
# 表示错误

下面是一个简单的 Marble 语句,描述了第一个 Observable,它在 Observer 订阅后的第二、四、六帧时发出值,并在第六帧后完成。


^--aaa|

现在我们对 Marble 语法有了一个初步的认识,接下来我们思考一下,该如何利用 Marble 语法来测试异步代码呢?

假设一个虚拟时间单位是 1 ms,我们可以像下面这样写:


-a------c---|

为了编写测试,我们需要一个 TestScheduler 实例。

下面的示例通过定义 hot Observable 演示了对上面示例中 Observable 的测试。


import { TestScheduler } from 'rxjs/testing'
import { throttleTime } from 'rxjs/operators'
const expect = require('chai').expect
const testScheduler = new TestScheduler((actual, expected) => {
  expect(actual).deep.equal(expected);
});
it('Test uppercase', () => {
  testScheduler.run(helpers => {
    const { hot, expectObservable } = helpers;
    const values = {
      a: 'A',
      b: 'B',
      c: 'C'
    }
    var characterObservable$ = hot('-a--b--c---|', values);
    var expectedMarbleVisual =      '-a-----c---|';
    expectObservable(characterObservable$.pipe(throttleTime(3, testScheduler))).toBe(expectedMarbleVisual, values);
  });
});

如果我们想要精确地测试不同用例的时间该怎么办?Marble 也支持下面这种语法:


499ms a 499ms b 999ms (c|)

上面这段 Marble 语句表示:在订阅 Observable 500 毫秒后,发出第一个值,再过 500 毫秒后发出第二个值,再间隔 1000 毫秒,发出第三个值。其中我们在每个表示时间的位置都减少了 1 毫秒,原因是发出值本身需要 1 毫秒。



相关文章
|
7天前
|
JSON Java 测试技术
SpringCloud2023实战之接口服务测试工具SpringBootTest
SpringBootTest同时集成了JUnit Jupiter、AssertJ、Hamcrest测试辅助库,使得更容易编写但愿测试代码。
36 3
|
1月前
|
JSON 算法 数据可视化
测试专项笔记(一): 通过算法能力接口返回的检测结果完成相关指标的计算(目标检测)
这篇文章是关于如何通过算法接口返回的目标检测结果来计算性能指标的笔记。它涵盖了任务描述、指标分析(包括TP、FP、FN、TN、精准率和召回率),接口处理,数据集处理,以及如何使用实用工具进行文件操作和数据可视化。文章还提供了一些Python代码示例,用于处理图像文件、转换数据格式以及计算目标检测的性能指标。
59 0
测试专项笔记(一): 通过算法能力接口返回的检测结果完成相关指标的计算(目标检测)
|
2月前
|
移动开发 JSON Java
Jmeter实现WebSocket协议的接口测试方法
WebSocket协议是HTML5的一种新协议,实现了浏览器与服务器之间的全双工通信。通过简单的握手动作,双方可直接传输数据。其优势包括极小的头部开销和服务器推送功能。使用JMeter进行WebSocket接口和性能测试时,需安装特定插件并配置相关参数,如服务器地址、端口号等,还可通过CSV文件实现参数化,以满足不同测试需求。
239 7
Jmeter实现WebSocket协议的接口测试方法
|
2月前
|
JSON 移动开发 监控
快速上手|HTTP 接口功能自动化测试
HTTP接口功能测试对于确保Web应用和H5应用的数据正确性至关重要。这类测试主要针对后台HTTP接口,通过构造不同参数输入值并获取JSON格式的输出结果来进行验证。HTTP协议基于TCP连接,包括请求与响应模式。请求由请求行、消息报头和请求正文组成,响应则包含状态行、消息报头及响应正文。常用的请求方法有GET、POST等,而响应状态码如2xx代表成功。测试过程使用Python语言和pycurl模块调用接口,并通过断言机制比对实际与预期结果,确保功能正确性。
247 3
快速上手|HTTP 接口功能自动化测试
|
1月前
|
JavaScript 前端开发 API
vue尚品汇商城项目-day02【9.Home组件拆分+10.postman测试接口】
vue尚品汇商城项目-day02【9.Home组件拆分+10.postman测试接口】
40 0
|
2月前
|
JavaScript 前端开发 测试技术
ChatGPT与接口测试
ChatGPT与接口测试,测试通过
48 5
|
3月前
|
网络协议 测试技术 网络安全
Python进行Socket接口测试的实现
在现代软件开发中,网络通信是不可或缺的一部分。无论是传输数据、获取信息还是实现实时通讯,都离不开可靠的网络连接和有效的数据交换机制。而在网络编程的基础中,Socket(套接字)技术扮演了重要角色。 Socket 允许计算机上的程序通过网络进行通信,它是网络通信的基础。Python 提供了强大且易于使用的 socket 模块,使开发者能够轻松地创建客户端和服务器应用,实现数据传输和交互。 本文将深入探讨如何利用 Python 编程语言来进行 Socket 接口测试。我们将从基础概念开始介绍,逐步引导大家掌握创建、测试和优化 socket 接口的关键技能。希望本文可以给大家的工作带来一些帮助~
|
4月前
|
存储
Postman 接口测试配置 Pre-request Script
Postman 接口测试配置 Pre-request Script
212 5
Postman 接口测试配置 Pre-request Script
|
3月前
|
网络协议 测试技术 网络安全
Python进行Socket接口测试的实现
在现代软件开发中,网络通信是不可或缺的一部分。无论是传输数据、获取信息还是实现实时通讯,都离不开可靠的网络连接和有效的数据交换机制。而在网络编程的基础中,Socket(套接字)技术扮演了重要角色。 Socket 允许计算机上的程序通过网络进行通信,它是网络通信的基础。Python 提供了强大且易于使用的 socket 模块,使开发者能够轻松地创建客户端和服务器应用,实现数据传输和交互。 本文将深入探讨如何利用 Python 编程语言来进行 Socket 接口测试。我们将从基础概念开始介绍,逐步引导大家掌握创建、测试和优化 socket 接口的关键技能。希望本文可以给大家的工作带来一些帮助~
|
3月前
|
SQL Java 测试技术
SpringBoot单元测试快速写法问题之PorkService 接口中的 getPork 方法的作用如何解决
SpringBoot单元测试快速写法问题之PorkService 接口中的 getPork 方法的作用如何解决

热门文章

最新文章