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

9b753d898d93aae8bd163db5c420a1ae?s=47 Jerry Hong
September 21, 2017

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

在日常生活中,我們常常用 「畫圖」 來協助溝通,圖像可以簡單的幫助我們清楚表達,整個思考流程與邏輯;那單元測試作為程式碼的文件,為什麼我們不乾脆用「畫圖」來撰寫單元測試呢?

本次分享會說明如何用 Marble Diagram 來撰寫 Observable 的測試,以及使用 Marble Testing 所帶來的好處。適合用過 RxJS 或其相關 Library (recompose, redux-observable) 的開發者 或是對 RxJS 有興趣的初學者

9b753d898d93aae8bd163db5c420a1ae?s=128

Jerry Hong

September 21, 2017
Tweet

Transcript

  1. 6.

    var mouseMove = Observable .fromEvent(DOM, 'mousemove'); var subscription = mouseMove

    .subscribe(x !=> console.log(x)); subscription.unsubscribe(); Observable ୌ缏膏懪褂
  2. 8.

    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: () !=> {}, });
  3. 9.

    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: () !=> {}, });
  4. 10.

    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: () !=> {}, });
  5. 11.

    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: () !=> {}, });
  6. 12.

    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: () !=> {}, });
  7. 13.
  8. 15.

    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|)
  9. 19.

    Observable.interval(10) .take(3) .map(x !=> x + 1) .filter(x !=> x

    % 2 ##=== 0) -012.. -012| --2| -01(2|) -12(3|)
  10. 20.

    -a-bc-d--- • 10 frames 碻牧蝑ڊ a • 30 frames 碻牧蝑ڊ

    b • 40 frames 碻牧蝑ڊ c • 60 frames 碻牧蝑ڊ d • ䷱磪奾๳
  11. 21.

    --a-b-(c|) • 20 frames 碻牧蝑ڊ a • 40 frames 碻牧蝑ڊ

    b • 50 frames 碻牧蝑ڊ c ޾ complete
  12. 27.
  13. 28.
  14. 29.
  15. 30.

    Testing Asynchronous Code is Hard • ࣁ秇硈覍ݶྍԪկ碻覍ଉ蕦褾牧ᘒӬ਻ฃ䌃梊 • ইຎ౯㮉ጱ㻌ز介手๜蛪疰磪梊藮牧ᮎ㻌ز介手疰䷱磪఺嬝 •

    ؃ই౯㮉犋秇硈碻樌牧ፗ矑狶羊嘦介手牧㳷䨝蝨౮介手虋஑覍ଉ眸 • 疪ٌࣁ౯㮉ᥝ瞱媲瑿᪒介手碻
  16. 32.
  17. 33.

    Scheduler • Scheduler ฎአ㬵矒ګԪկ咳ኞጱ碻秚 • ইຎ Observable 磪戔ਧ Scheduler 䨝಩䁆ᤈ

    next, error, complete ጱ ොဩԻ妔 Scheduler 䁆ᤈ • Scheduler ٌ䋿疰蚤 setTimeout, setInterval 狶Ӟ䰬ጱԪ眐 • አ㬵戔ਧ礓կԪ眐ࣁ礓㮆碻樌讨藉咳
  18. 35.

    RxJS 5 - Scheduler • queue - 䨝ض಩ྯ稞ጱ犨率獊蟂硯ک queue 愊ٚ

    ݶྍ 蒂ቘ牐 • async - ݶ setInterval牧ಅ磪 timer-base observable ጱ毆戔独 • asap - ݶ setImmediate牧䨝穉 async ๅ盠 
 (Node 絑हӥ犋Ӟਧ牧ਥො෈կฎ梊ጱ牐ݝ磪ࣁ node v0.9 犥獮䨝ฎአ process.nextTick牧v0.9 ԏ盅᮷ฎአ setImmediate牐ԏ盅ݢ胼䨝狕ྋ) • animationFrame - ݶ requestAnimationFrame
  19. 38.

    TestScheduler • TestScheduler ฎ䌕槹戔懯㬵狶介手አጱ Scheduler • TestScheduler 䨝秇硈碻樌牧㪔ୌ缏 100% ݢ᯿蕦ጱ介手

    • ࢩ傶碻樌ฎ蒅硈ڊ㬵ጱ (virtual Time)牧ಅ磪ጱԪ眐䨝缏ܨ䁆ᤈ牧犋 襑ᥝ፥ጱ臺碻樌缛盃
  20. 39.

    let testScheduler = new TestScheduler( (actual, expected) !=> expect(actual).toEqual(expected) );

    ୌ缏 Scheduler • 蝚螂 new 橕棎ਁ㬵ୌ缏 Scheduler • 㯽獈Ӟ㮆 callback牧㪔ࣁ callback Ӿ矑硩毆๗ጱ独 膏䋿褬ጱ独牧狶穉䌘 (deepEqual) • ဳ఺物ྯ㮆介手㻌زጱ testScheduler ஠殾ฎ加缏 ጱ䋿ֺ牧玽㳷䨝磪 side effect
  21. 40.

    it('of', () !=> { const actual = Observable .of('Jerry', testScheduler);

    testScheduler .expectObservable(actual) .toBe('(a|)', { a: 'Jerry' }); testScheduler.flush(); }); 介手 observable • ୌ缏 Observable 㪔戔 ਧ Scheduler 傶 testScheduler • አ testScheduler ጱ expectObservable 狶 䥁᥺ • 䁆ᤈ flush()
  22. 44.
  23. 46.

    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
  24. 49.

    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
  25. 50.

    愆獅 • ኧ Promise 旉౮ጱ Observable 犋胼አ marble testing牧ݝ胼 mock

    ധ牧౲ฎፗ矑 subscribe 介手牐(#701) • ፓ獮蚤 timer ፘ橕ጱ operator (delay, throttle, debounce) 襑ᥝᛔᤈℂ Observable.prototype 狶 mock • 種㺔氂䨝ࣁ v5.5, 5.6 വڊ lettable 粬௔盅薹究 • redux-observable-test-helper 磪൉׀