RxJS修煉之 用彈珠測試學習RxJS

羅裏吧嗦的一些解釋

RxJS使用的愈來愈多,但發現不少開發者都是使用最基礎的部分用來處理http請求,其實RxJS能夠作的事情不只僅是在對網絡資源處理過程當中替代Promise,但若是按照一些已有的網絡博客和分享來看,對兩者在實踐上的差別確實體現的不明顯,因此想從測試的角度,和你們一塊兒理解RxJS,發現它更大的威力。html

另外其實本人其實是在網絡上本身學習過一些RxJS的基本概念和使用,並在項目上小小嚐試過RxJS,只是以爲嘗試的不夠完全,建議看這篇文章的時候最好仍是對RxJS的基本概念有一個大的瞭解。node

此文做爲對RxJS有了大概瞭解後,從另外一個觀察角度去了解RxJS的一個分享。git

測試,什麼樣的測試?

接觸過測試的人可能立刻會想知道,你說的是什麼測試?在測試金字塔的哪一層?能夠TDD嗎?和咱們以前瞭解的測試有什麼特別不同的?github

我說的測試叫彈珠測試(Marble Tests),它屬於底層的單元測試級別,主要用於針對自定義操做符的測試,能夠TDD,比較特別的算是它是基於DSL的,你必須瞭解它的DSL以後才能開始寫測試。typescript

關於如何寫彈珠測試,在官方github上面也有一些文檔能夠參考,但不是特別詳細,無法像一個框架的quick start幫助你們起步。我會嘗試和你們一塊兒動手來寫這些測試(從最基本的環境搭建開始),不會步步到位,可是關鍵步驟都有。網絡

看第一個測試?

如下是官網隨便找的一個測試,一個簡單的map你能夠記住你看完這個測試的感覺。app

asDiagram('map(x => 10 * x)')('should map multiple values', function () {
        var a = cold('--1--2--3--|');
        var asubs = '^          !';
        var expected = '--x--y--z--|';
        var r = a.map(function (x) { return 10 * x; });
        expectObservable(r).toBe(expected, { x: 10, y: 20, z: 30 });
        expectSubscriptions(a.subscriptions).toBe(asubs);
    });

我並不知道你的感覺,我第一眼是有點懵的反正,緣由也很簡單爲何出現 | ^ - 這些字符,它們在這裏是幹什麼的? 這個時候要放出DSL這個大招了。框架

學彈珠測試的DSL

前面咱們隨意找的一個測試,彷佛並不符合測試語義化這一點,實際上是由於咱們沒有理解它所使用的DSL,此處的DSL能夠理解爲編寫彈珠測試的時候使用的一種特定的語言,是基於彈珠測試的上下文可讓機器懂得你語義的一種語言。單元測試

咱們須要簡單介紹下彈珠測試所使用的DSL中的一些基本知識(此部分信息摘自cn.rx.js.org學習

首先彈珠語法是用字符串表示隨「時間」流逝而發生的事件。任何彈珠字符串的首字符永遠都表示「零幀」。「幀」是有點相似於虛擬毫秒的概念。

基本的彈珠語法

  • "-" 時間: 10「幀」的時間段。
  • "|" 完成: 表示 Observalbe 成功完成。這是 Observable 生產者所發出的 complete() 信號。
  • "#" 錯誤: 終止 Observable 的錯誤。 這是 Observable 生產者所發出的 error() 信號。
  • "a" 任意字符: 全部其餘字符表示由 Observalbe 生產者所發出的 next() 信號的值。
  • "()" 同步分組: 當多個事件須要在同一幀中同步地發出,用圓括號來將這些事件彙集在一塊兒。你能夠以這種形式來聚合值、完成或錯誤。 起始 ( 的位置決定了值發出的時間。
  • "^" 訂閱時間點: (只適用於熱的 Observabe) 顯示測試 Observable 訂閱熱的 Observable 的點。它是此 Observable 的「零幀」,在 ^ 前的全部幀都將是無效的。

Subscription 的彈珠語法

  • "-" 時間: 10「幀」的時間段。
  • "^" 訂閱時間點: 顯示訂閱發生的時間點。
  • "!" 取消訂閱時間點: 顯示取消訂閱發生的時間點。

因此咱們嘗試逐行理解下前面出現的測試

asDiagram('map(x => 10 * x)')('should map multiple values', function () {
       ***
    });

asDiagram是指基於測試生成 PNG 彈珠圖,生成彈珠圖的原理是根據一些結構化的信息,加上一些如imagemagick的庫,就能夠生成以下的圖了,更多的操做符對應的彈珠圖例子能夠再rxmarbles.com找到。
圖片描述

var a = cold('--1--2--3--|');
 var asubs = '^          !';
 var expected = '--x--y--z--|';
 var r = a.map(function (x) { return 10 * x; });
 expectObservable(r).toBe(expected, { x: 10, y: 20, z: 30 });
 expectSubscriptions(a.subscriptions).toBe(asubs);

這個測試的步驟是這樣的

  • 建立一個 Observable a,a在第30幀傳入1,第60幀傳入2,第90幀傳入3,第120幀complete。
  • 對a進行map操做的方法r,r將a中的每一個值變爲原來的10倍
  • 期待對a的進行方法r的操做後,在第30幀收到10,第60幀收到20,第90幀收到30,第120幀結束
  • 期待對a的訂閱在第10幀開始,在第120幀結束

本身搭建環境?

剛剛是咱們用官網的例子結合一些輔助網站的資料,對彈珠測試進行的簡單的瞭解,下面咱們開始本身搭建一個能夠本身寫彈珠測試、運行測試的環境。

咱們先使用和官網同樣的第三方依賴建立環境,等咱們慢慢熟悉這套以後,再換用其餘第三方的依賴搭建環境。
ready go!

首先咱們建立一個ts項目(最近ts寫多了),並使用yarn安裝基本的測試依賴。

"dependencies": {
    "@types/chai": "^4.0.10",
    "@types/mocha": "^2.2.45",
    "chai": "^4.1.2",
    "mocha": "^4.0.1",
    "rxjs": "^5.5.6",
    "ts-node": "^4.1.0",
    "typescript": "^2.6.2"
  },
  "scripts": {
    "test": "TS_NODE_FAST=true mocha --compilers ts:ts-node/register --opts spec/support/coverage.opts \"specs/**/*.spec.ts\""
  }

而後我依樣畫瓢的把對TestScheduler的包裝方法copy了下,中間遇到一些寫法不同的部分稍做調整。

import { TestScheduler, Observable } from 'rxjs';
import { SubscriptionLog } from 'rxjs/src/testing/SubscriptionLog';
import { ColdObservable } from 'rxjs/src/testing/ColdObservable';
import { HotObservable } from 'rxjs/src/testing/HotObservable';
export type observableToBeFn = (marbles: string, values?: any, errorValue?: any) => void;
export type subscriptionLogsToBeFn = (marbles: string | string[]) => void;

const testScheduler =  new TestScheduler(null);
export function hot(marbles: string, values?: any, error?: any): HotObservable<any> {
  return testScheduler.createHotObservable.apply(testScheduler, arguments);
}

export function cold(marbles: string, values?: any, error?: any): ColdObservable<any> {
  return testScheduler.createColdObservable.apply(testScheduler, arguments);
}

export function expectObservable(observable: Observable<any>,
                                 unsubscriptionMarbles: string = null): ({ toBe:observableToBeFn }) {
  return testScheduler.expectObservable.apply(testScheduler, arguments);
}

export function expectSubscriptions(actualSubscriptionLogs: SubscriptionLog[]): ({ toBe: subscriptionLogsToBeFn }) {
  return testScheduler.expectSubscriptions.apply(testScheduler, arguments);
}

export function time(marbles: string): number {
  return testScheduler.createTime.apply(testScheduler, arguments);
}

這樣基本的hot cold方法就可使用啦!

圖片描述

下一篇 Fancy的彈珠圖

彈珠測試之因此能稱之爲彈珠測試,從字面意思上很容易猜想和彈珠圖相關。咱們已經有一個基本的測試了,下一篇咱們開始把它變成彈珠圖吧。

相關文章
相關標籤/搜索