- RxJS Marble Testing
Observable
What is Observable ?
What is Observable ?
Collection+ Time
Observable 

What is Observable ?
var mouseMove = Observable
.fromEvent(DOM, 'mousemove');
var subscription = mouseMove
.subscribe(x !=> console.log(x));
subscription.unsubscribe();
Observable
Observable.of(2, 3, 4)
Observable.from([2, 3, 4])
Observable.fromEvent(DOM, 'click')
Observable.interval(1000)
Observable.empty()
Observable.never()
Observable.throw()
Observable
var sub = Observable
.from([1, 2, 3])
.map(x !=> x + 1)
.filter(x !=> x % 2 ##=== 0)
.subscribe({
next: x !=> console.log(x),
error: err !=> {},
complete: () !=> {},
});
observable
• Observable
•
• (subscribe)
• operator
var sub = Observable
.from([1, 2, 3])
.map(x !=> x + 1)
.filter(x !=> x % 2 ##=== 0)
.subscribe({
next: x !=> console.log(x),
error: err !=> {},
complete: () !=> {},
});
operator
• Observable
•
•
observable
var sub = Observable
.from([1, 2, 3])
.map(x !=> x + 1)
.filter(x !=> x % 2 ##=== 0)
.subscribe({
next: x !=> console.log(x),
error: err !=> {},
complete: () !=> {},
});
observer
• observable
• next 

error complete
var sub = Observable
.from([1, 2, 3])
.map(x !=> x + 1)
.filter(x !=> x % 2 ##=== 0)
.subscribe({
next: x !=> console.log(x),
error: err !=> {},
complete: () !=> {},
});
subscription
• observable
•
•
(unsubscribe)
var sub = Observable
.from([1, 2, 3])
.map(x !=> x + 1)
.filter(x !=> x % 2 ##=== 0)
.subscribe({
next: x !=> console.log(x),
error: err !=> {},
complete: () !=> {},
});
Marble Diagram
--a--b--c--d--e|
Marble Diagram
• - : (10 frames)
• n(0-9/a-z): (next)
• |: (complete)
• #: (error)
• ():
time
----0---1---2---3--
----0---1---2---3|
----0---1---2---3--#
(123|)
Observable.of(1, 2, 3)
Observable.from([1, 2, 3])
(123|)
Observable.interval(10) -0123..
Observable
.fromEvent(DOM, 'click') ---e--ee-e--e-...
Observable.interval(10)
.take(3)
.map(x !=> x + 1)
.filter(x !=> x % 2 ##=== 0)
-012..
-012|
--2|
-01(2|)
-12(3|)
-a-bc-d---
• 10 frames a
• 30 frames b
• 40 frames c
• 60 frames d
•
--a-b-(c|)
• 20 frames a
• 40 frames b
• 50 frames c complete
marble diagram observable
marble diagram 

observable
Jerry Hong
Front End Engineer |
Website: blog.jerry-hong.com
Facebook: J.H.MingChen
Testing Asynchronous Code
is Hard
Testing Asynchronous Code is Hard
• 











Testing Asynchronous Code is Hard
•
•
•
•
Marble Testing
Scheduler
Scheduler
• Scheduler
• Observable Scheduler next, error, complete
Scheduler
• Scheduler setTimeout, setInterval
•
const sub = Scheduler.async.schedule(() !=> {
console.log('123');
}, 100);
sub.unsubscribe();
RxJS 5 - Scheduler
• queue - queue
• async - setInterval timer-base observable
• asap - setImmediate async 

(Node node v0.9 process.nextTick v0.9 setImmediate )
• animationFrame - requestAnimationFrame
Observable.of(0, 1, 2, Scheduler.async);
Observable.from([0, 1, 2], Scheduler.async);
Observable.interval(0, Scheduler.animationFrame);
TestScheduler
TestScheduler
• TestScheduler Scheduler
• TestScheduler 100%
• (virtual Time)
let testScheduler = new TestScheduler(
(actual, expected) !=>
expect(actual).toEqual(expected)
);
Scheduler
• new
Scheduler
• callback
callback
(deepEqual)
•
testScheduler
side
effect
it('of', () !=> {
const actual = Observable
.of('Jerry', testScheduler);
testScheduler
.expectObservable(actual)
.toBe('(a|)', { a: 'Jerry' });
testScheduler.flush();
});
observable
• Observable
Scheduler
testScheduler
• testScheduler
expectObservable
• flush()
const obs = testScheduler
.createColdObservable(
'%%---u|',
{ u: 'http:%//google.com'}
);
mock observable
const obs = testScheduler
.createColdObservable(
'%%---#',
null,
new Error('test')
);
mock observable
with error
const time = testScheduler
.createTime('%%---|');
createTime
Demo
Redux-Observable
export const show = () !=> ({ type: 'SHOW' });
export const close = data !=> ({ type: 'CLOSE' });
export const delayEpic = action$ !=>
action$.ofType('SHOW').delay(3000).map(x !=> close());
Action Creator & Epic
redux-observable-test-helper
import {
createExpectedEpic,
} from 'redux-observable-test-helper';
const expectedEpic = createExpectedEpic(
(actual, expected) !=> {
expect(actual).toEqual(expected);
}
);
export const show = () !=>
({ type: 'SHOW' });
export const close = () !=>
({ type: 'CLOSE' });
export const delayEpic = action$ !=>
action$.ofType('SHOW')
.delay(3000)
.map(x !=> close());
test('test delay', () !=> {
expectedEpic(
delayEpic,
{
action: ['a', { a: show() }],
expect: ['(--a', { a: close() }],
},
mockDelay('(--|')
);
});
reducer.js reducer.test.js
• Promise Observable marble testing mock
subscribe (#701)
• timer operator (delay, throttle, debounce)
Observable.prototype mock
• v5.5, 5.6 lettable
• redux-observable-test-helper

如何「畫圖」寫測試 - RxJS Marble Test