Upgrade to Pro — share decks privately, control downloads, hide ads and more …

如何「畫圖」寫測試 - 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. ই֜̿向瑽̀䌃介手 - RxJS Marble Testing

  2. Observable 墋Օ

  3. What is Observable ?

  4. What is Observable ? Collection + Time

  5. Observable 疰猟ฎӞ㮆ଧڜ牧
 愊ᶎጱزᔰ䨝褰茐碻樌വ蝑 What is Observable ?

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

    .subscribe(x !=> console.log(x)); subscription.unsubscribe(); Observable ୌ缏膏懪褂
  7. Observable.of(2, 3, 4) Observable.from([2, 3, 4]) Observable.fromEvent(DOM, 'click') Observable.interval(1000) Observable.empty()

    Observable.never() Observable.throw() ୌ缏 Observable
  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: () !=> {}, });
  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: () !=> {}, });
  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: () !=> {}, });
  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: () !=> {}, });
  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: () !=> {}, });
  13. None
  14. Marble Diagram --a--b--c--d--e|

  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|)
  16. Observable.of(1, 2, 3) Observable.from([1, 2, 3]) (123|)

  17. Observable.interval(10) -0123..

  18. Observable .fromEvent(DOM, 'click') ---e--ee-e--e-...

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

    % 2 ##=== 0) -012.. -012| --2| -01(2|) -12(3|)
  20. -a-bc-d--- • 10 frames 碻牧蝑ڊ a • 30 frames 碻牧蝑ڊ

    b • 40 frames 碻牧蝑ڊ c • 60 frames 碻牧蝑ڊ d • ䷱磪奾๳
  21. --a-b-(c|) • 20 frames 碻牧蝑ڊ a • 40 frames 碻牧蝑ڊ

    b • 50 frames 碻牧蝑ڊ c ޾ complete
  22. 碪簁౯㮉ݢ犥አ marble diagram 㬵蔭纈 observable

  23. ᮎ傶Ջ讕౯㮉犋Ԧ脀አ marble diagram 
 㬵䌃 observable ጱ㻌ز介手珌牫

  24. Jerry Hong Front End Engineer | Website: blog.jerry-hong.com Facebook: J.H.MingChen

  25. Testing Asynchronous Code is Hard

  26. Testing Asynchronous Code is Hard • ࣁ秇硈覍ݶྍԪկ碻覍ଉ蕦褾牧ᘒӬ਻ฃڊ梊
 
 
 


    
 

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

    ؃ই౯㮉犋秇硈碻樌牧ፗ矑狶羊嘦介手牧㳷䨝蝨౮介手虋஑覍ଉ眸 • 疪ٌࣁ౯㮉ᥝ瞱媲瑿᪒介手碻
  31. 樄ত拻 Marble Testing ԏ獮牧౯㮉஠殾ض啻薹

  32. Scheduler

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

    next, error, complete ጱ ොဩԻ妔 Scheduler 䁆ᤈ • Scheduler ٌ䋿疰蚤 setTimeout, setInterval 狶Ӟ䰬ጱԪ眐 • አ㬵戔ਧ礓կԪ眐ࣁ礓㮆碻樌讨藉咳
  34. const sub = Scheduler.async.schedule(() !=> { console.log('123'); }, 100); sub.unsubscribe();

  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
  36. Observable.of(0, 1, 2, Scheduler.async); Observable.from([0, 1, 2], Scheduler.async); Observable.interval(0, Scheduler.animationFrame);

    ֵአොୗ
  37. TestScheduler

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

    • ࢩ傶碻樌ฎ蒅硈ڊ㬵ጱ (virtual Time)牧ಅ磪ጱԪ眐䨝缏ܨ䁆ᤈ牧犋 襑ᥝ፥ጱ臺碻樌缛盃
  39. let testScheduler = new TestScheduler( (actual, expected) !=> expect(actual).toEqual(expected) );

    ୌ缏 Scheduler • 蝚螂 new 橕棎ਁ㬵ୌ缏 Scheduler • 㯽獈Ӟ㮆 callback牧㪔ࣁ callback Ӿ矑硩毆๗ጱ独 膏䋿褬ጱ独牧狶穉䌘 (deepEqual) • ဳ఺物ྯ㮆介手㻌زጱ testScheduler ஠殾ฎ加缏 ጱ䋿ֺ牧玽㳷䨝磪 side effect
  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()
  41. const obs = testScheduler .createColdObservable( '%%---u|', { u: 'http:%//google.com'} );

    mock observable
  42. const obs = testScheduler .createColdObservable( '%%---#', null, new Error('test') );

    mock observable with error
  43. const time = testScheduler .createTime('%%---|'); createTime

  44. Demo

  45. Redux-Observable

  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
  47. redux-observable-test-helper

  48. import { createExpectedEpic, } from 'redux-observable-test-helper'; const expectedEpic = createExpectedEpic(

    (actual, expected) !=> { expect(actual).toEqual(expected); } );
  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
  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 磪൉׀