Marble testing is an expressive way to test observables by utilizing marble diagrams. This lesson will walk you through the syntax and features, preparing you to start writing marble tests today!

Grep two files from the rxjs

  • https://github.com/ReactiveX/rxjs/blob/master/spec/helpers/marble-testing.ts
  • https://github.com/ReactiveX/rxjs/blob/master/spec/helpers/test-helper.ts
/*
RxJS marble testing allows for a more natural style of testing observables.
To get started, you need to include a few helpers libraries, marble-testing.ts and test-helper.ts,
in your karma.conf or wallaby.js configuration file.
These files provide helpers for parsing marble diagrams and asserting against the subscription points and result
of your observables under test. For these examples I will be using Jasmine, but Mocha and Chai works just as well. Let's get started with the basics of marble testing! First, let's understand the pieces that make up a valid marble diagram. Dash: Indicates a passing of time, you can think of each dash as 10ms when it comes to your tests.
----- <----- 50ms
Characters: Each character inside the dash indicates an emission.
-----a-----b-----c <----- Emit 'a' at 60ms, 'b' at 120ms, 'c' at 180ms
Pipes |: Pipes indicate the completion point of an observable.
-----a| <----- Emit 'a' at 60ms then complete (70ms)
Parenthesis (): Parenthesis indicate multiple emissions in same time frame, think Observable.of(1,2,3)
-----(abc|) <----- Emit 'a''b''c' at 60ms then complete (60ms)
Caret ^: Indicates the starting point of a subscription, used with expectSubscription assertion.
^------- <----- Subscription point at caret.
Exclamation Point - !: Indicates the end point of a subscription, also used with expectSubscription assertion.
^------! <----- Subscription starts at caret, ends at exclamation point.
Pound Sign - #: Indicates an error
---a---# <----- Emit 'a' at 40ms, error at 80ms
There are also a few methods included to parse marble sequences and transpose values. cold(marbles: string, values?: object, error?: any) : Subscription starts when test begins
cold(--a--b--|, {a: 'Hello', b: 'World'}) <----- Emit 'Hello' at 30ms and 'World' at 60ms, complete at 90ms
hot(marbles: string, values?: object, error?: any) : Behaves like subscription starts at point of caret
hot(--^--a---b--|, {a: 'Goodbye', b: 'World'}) <----- Subscription begins at point of caret
*/

For example we want to test:

const source =       "---a---b---c--|";
const expected = "---a---b---c--|";

they should be equal.

Here each '-' means 1. frames.

'|' means completed.

The method we need to use is 'expectObservable' & 'cold':

    it('should parse marble diagrams', () => {
const source = cold('---a---b---c---|');
const expected = '---a---b---c---|'; expectObservable(source).toBe(expected)
});

Cold will treat the beginning of the diagram as a subscription point. Now the test passing.

But if we change a little bit:

    it('should parse marble diagrams', () => {
const source = cold('---a---b---c---|');
const expected = '---a--b---c---|'; expectObservable(source).toBe(expected)
});

It reports error:

    Expected
{"frame":30,"notification":{"kind":"N","value":"a","hasValue":true}}
{"frame":,"notification":{"kind":"N","value":"b","hasValue":true}}
{"frame":,"notification":{"kind":"N","value":"c","hasValue":true}}
{"frame":,"notification":{"kind":"C","hasValue":false}} to deep equal
{"frame":30,"notification":{"kind":"N","value":"a","hasValue":true}}
{"frame":,"notification":{"kind":"N","value":"b","hasValue":true}}
{"frame":,"notification":{"kind":"N","value":"c","hasValue":true}}
{"frame":,"notification":{"kind":"C","hasValue":false}}

Test 'concat' opreator:

    it('should work with cold observables', () => {
const obs1 = cold('-a---b-|');
const obs2 = cold('-c---d-|');
const expectedConcatRes = '-a---b--c---d-|'; expectObservable(obs1.concat(obs2)).toBe(expectedConcatRes)
});

'Hot' observable: Hot will actually let you identify the subscription point yourself:

When testing hot observables you can specify the subscription point using a caret '^', similar to how you specify subscriptions when utilizing the expectSubscriptions assertion.

    it('should work with hot observables', () => {
const obs1 = hot('---a--^--b---|');
const obs2 = hot('-----c---^-----------------d-|');
const expected = '---b--------------d-|'; expectObservable(obs1.concat(obs2)).toBe(expected);
});

Algin the ^, easy for read

Spread subscription and marble diagram:

    /*
For certain operators you may want to confirm the point at which
an observable is subscribed or unsubscribed. Marble testing makes this
possible by using the expectSubscriptions helper method. The cold and hot
methods return a subscriptions object, including the frame at which the observable
would be subscribed and unsubscribed. You can then assert against these
subscription points by supplying a diagram which indicates the expected behavior. ^ - Indicated the subscription point.
! - Indicates the point at which the observable was unsubscribed. Example subscriptions object: {"subscribedFrame":70,"unsubscribedFrame":140}
*/
it('should identify subscription points', () => {
const obs1 = cold('-a---b-|');
const obs2 = cold('-c---d-|')
const expected = '-a---b--c---d-|';
const sub1 = '^------!'
const sub2 = '-------^------!' expectObservable(obs1.concat(obs2)).toBe(expected);
expectSubscriptions(obs1.subscriptions).toBe(sub1);
expectSubscriptions(obs2.subscriptions).toBe(sub2);
})

Object to map the key and value:

    /*
Both the hot and cold methods, as well the the toBe method accept an object map as a
second parameter, indicating the values to output for the appropriate placeholder.
When the test is executed these values rather than the matching string in the marble diagram.
*/
it('should correctly sub in values', () => {
const values = {a: 3, b: 2};
const source = cold( '---a---b---|', values);
const expected = '---a---b---|'; expectObservable(source).toBe(expected, values);
});
    /*
Multiple emissions occuring in same time frame can be represented by grouping in parenthesis.
Complete and error symbols can also be included in the same grouping as simulated outputs.
*/
it('should handle emissions in same time frame', () => {
const obs1 = Observable.of(1,2,3,4);
const expected = '(abcd|)'; expectObservable(obs1).toBe(expected, {a: 1, b: 2, c: 3, d: 4});
});
    /*
For asynchronous tests RxJS supplies a TestScheduler.
How it works...
*/
it('should work with asynchronous operators', () => {
const obs1 = Observable
.interval(10, rxTestScheduler)
.take(5)
.filter(v => v % 2 === 0);
const expected = '-a-b-(c|)'; expectObservable(obs1).toBe(expected, {a: 0, b: 2, c: 4});
});

Error handling:

    /*
Observables that encounter errors are represented by the pound (#) sign.
In this case, our observable is retried twice before ultimately emitting an error.
A third value can be supplied to the toBe method specifying the error to be matched.
*/
it('should handle errors', () => {
const source = Observable.of(1,2,3,4)
.map(val => {
if(val > 3){
throw 'Number too high!';
};
return val;
})
.retry(2); const expected = '(abcabcabc#)'; expectObservable(source).toBe(expected, {a: 1, b: 2, c: 3, d: 4}, 'Number too high!');
});

[RxJS] Introduction to RxJS Marble Testing的更多相关文章

  1. [AngularJS + RxJS] Search with RxJS

    When doing search function, you always need to consider about the concurrent requests. AEvent ----(6 ...

  2. [RxJS] Split an RxJS Observable into groups with groupBy

    groupBy() is another RxJS operator to create higher order observables. In this lesson we will learn ...

  3. [RxJS] Split an RxJS observable conditionally with windowToggle

    There are variants of the window operator that allow you to split RxJS observables in different ways ...

  4. [RxJS] Split an RxJS observable with window

    Mapping the values of an observable to many inner observables is not the only way to create a higher ...

  5. RxJS v6 学习指南

    为什么要使用 RxJS RxJS 是一套处理异步编程的 API,那么我将从异步讲起. 前端编程中的异步有:事件(event).AJAX.动画(animation).定时器(timer). 异步常见的问 ...

  6. RxJS速成 (上)

    What is RxJS? RxJS是ReactiveX编程理念的JavaScript版本.ReactiveX是一种针对异步数据流的编程.简单来说,它将一切数据,包括HTTP请求,DOM事件或者普通数 ...

  7. RxJS速成 (下)

    上一部分: http://www.cnblogs.com/cgzl/p/8641738.html Subject Subject比较特殊, 它即是Observable又是Observer. 作为Obs ...

  8. RxJS入门

    一.RxJS是什么? 官方文档使用了一句话总结RxJS: Think of RxJS as Lodash for events.那么Lodash主要解决了什么问题?Lodash主要集成了一系列关于数组 ...

  9. RxJS 6有哪些新变化?

    我们的前端工程由Angular4升级到Angular6,rxjs也要升级到rxjs6.  rxjs6的语法做了很大的改动,幸亏引入了rxjs-compact包,否则升级工作会无法按时完成. 按照官方的 ...

随机推荐

  1. adb remount 失败remount failed: Operation not permitted

    1. 进入shell adb shell 2. shell下输入命令 shell@android:/ $ sushell@android:/ # mount -o rw,remount -t yaff ...

  2. IOS NSNotificationCenter 通知的使用

    1.注册通知 [NSNotificationCenter defaultCenter] addObserver:self selector:@selector(notify) name:@" ...

  3. 《Python基础教程(第二版)》学习笔记 -> 第七章 更加抽象

    对象的魔力 多态:意味着可以对不同类的对象使用同样的操作: 封装:对外部世界隐藏对象的工作细节: 继承:以普通的类为基础建立专门的类对象 多态① 多态和方法绑定到对象特性上面的函数称为方法(metho ...

  4. java 复习003 之排序篇

    由java 复习003跳转过来的C语言实现版见some-sort-algorithms 快速排序(不稳定 O(n log n)) package vell.bibi.sort_algorithms; ...

  5. URAL-1982 Electrification Plan 最小生成树

    题目链接:http://acm.timus.ru/problem.aspx?space=1&num=1982 题意:无向图,给n个点,n^2条边,每条边有个一权值,其中有k个点有发电站,给出这 ...

  6. 终于弄好了 homework-09

    近一周的时间,顶着编译大作业严重搁置的压力,天天搞,终于把网页动态展示的搞出来了!恩,还挺好看~ 因为是最后一次作业了,也是因为天生的完美主义强迫症,做到自己满意才放心停下来.不过,这个过程,看着同学 ...

  7. homework-05 GoldNumberServer

    作业要求 这次作业要求实现一个黄金数游戏服务器,游戏流程如下,每个client向服务器提交一个有理数,服务器接收到所有客户端的提交后计算这些数字的平均数,再将其乘以黄金分割得到一个GoldNumber ...

  8. overflow,white-space,text-overflow组合技,省略号!

    这是一个很简单的组合技,灵活应用省略号.

  9. Hibernate之Session缓存以及操作Session缓存的相关方法

    1.Session概述 A.Session 接口是 Hibernate 向应用程序提供的操纵数据库的最主要的接口, 它提供了基本的保存, 更新, 删除和加载 Java 对象的方法. B. Sessio ...

  10. Web Service学习之一:Web Service原理

    一.定义 Web Service 不是框架也不是技术 而是解决远程调用.跨平台调用.跨语言调用问题的一种规范. 二.应用1.同一个公司新.旧系统的整合:比如CRM系统与OA.客服系统相互调用2.不同公 ...