Slide 1

Slide 1 text

使⽤用 Jest 進⾏行行 Front-End Unit Test Patrick Wang (patw) 2017/08/14 線上 React 讀書會版

Slide 2

Slide 2 text

Patrick Wang a.k.a. patw, ⼩小p • Front-end Developer • GitHub: github.com/patw0929 • Website: patw.me • Blog: blog.patw.me • Linkedin: www.linkedin.com/in/patrickpatw/

Slide 3

Slide 3 text

⼤大綱

Slide 4

Slide 4 text

• 前⾔言 • Frameworks • Jest 基本設定 • Enzyme • 語法與規則 • 各部分 Test 重點與⽅方法(以 react + redux 為例例) • 其他 jest ⼩小技巧 • Coverage

Slide 5

Slide 5 text

前⾔言

Slide 6

Slide 6 text

• 初嚐測試 • ⼯工作上的任務 • 資源、時間 • 新⼈人 • 團隊 • 單元測試、E2E 測試

Slide 7

Slide 7 text

https://github.com/patw0929/react-intl-tel-input 緣起:練習 & 在專案上掛⼀一些 badge 好棒棒! (⽽而且 Side/open source project 不會被 KPI 與組織流程綁架!)

Slide 8

Slide 8 text

趕案的⽇日常

Slide 9

Slide 9 text

https://www.youtube.com/watch?v=w7Myc8TP650 產品本⾝身的⽣生命週期 & 產品的品質有被重視嗎?

Slide 10

Slide 10 text

https://www.ptt.cc/bbs/Soft_Job/M.1475060406.A.1ED.html

Slide 11

Slide 11 text

內部分享 -> 組隊打怪

Slide 12

Slide 12 text

「這個 task 是值得投注⼯工時的,因為...」

Slide 13

Slide 13 text

https://testing.googleblog.com/2015/04/just-say-no-to-more-end-to-end-tests.html

Slide 14

Slide 14 text

No content

Slide 15

Slide 15 text

Frameworks

Slide 16

Slide 16 text

Frameworks • Unit Test • Jasmine • Mocha, Chai, Sinon • Jest • AVA

Slide 17

Slide 17 text

Assertion Libraries http://reactmad-testing.surge.sh/#/4

Slide 18

Slide 18 text

E2E Testing • Casper.js • Protractor • Nightwatch.js • CodeceptJS • Appium / Detox (Mobile App) http://reactmad-testing.surge.sh/#/5

Slide 19

Slide 19 text

Jest 基本設定

Slide 20

Slide 20 text

安裝 & 設定 jest & enable babel .babelrc 現在 create-react-app 也都會幫你建好 https://facebook.github.io/jest/docs/getting-started.html

Slide 21

Slide 21 text

npm scripts package.json 持續監看 產⽣生 coverage 結果 跑測試

Slide 22

Slide 22 text

jest configuration package.json 計入 coverage 的⽬目錄 在跑測試之前的環境準備設定 透過轉換幫助測試腳本讀的懂 ES7/SCSS 程式碼 測試腳本的檔名 pattern

Slide 23

Slide 23 text

jest configuration - setupFiles setupFiles 中除了了預設的 polyfill 外,
 還可設定其他的內容: 這邊是為了了 mock 掉 localStorage

Slide 24

Slide 24 text

jest configuration - transform config/jest/cssTransform.js If you want to test classname (CSS Modules)… e.g., styles.foobar === 'foobar' Notice that Proxy is enabled in Node.js v6.* by default; if you are not on Node v6.* yet, make sure you invoke Jest using node --harmony_proxies node_modules/.bin/jest.

Slide 25

Slide 25 text

Enzyme

Slide 26

Slide 26 text

Enzyme • by Airbnb • 封裝了了 React 的官⽅方測試⼯工具,擁有類似 jQuery 的 選擇器 API,測試撰寫上更更⽅方便便

Slide 27

Slide 27 text

Enzyme - find • 使⽤用 React TestUtils 選取特定⽬目標的 API:
 TestUtils.findRenderedDOMComponentWithXXXX • 使⽤用 Enzyme 只要 component.find(selector) 即可: http://airbnb.io/enzyme/docs/api/selector.html

Slide 28

Slide 28 text

Enzyme • shallow • render • mount

Slide 29

Slide 29 text

Enzyme - shallow • 封裝官⽅方的 shallow rendering • 只渲染第⼀一層,不渲染⼦子元件 • 速度快 • 不管 child component 的⾏行行為,測試重點只是 component 本⾝身

Slide 30

Slide 30 text

Enzyme - render • 類似 shallow,但使⽤用 Cheerio 引擎 • 不只渲染⼀一層 • API 跟 shallow 基本上⼀一致

Slide 31

Slide 31 text

Enzyme - mount • Full rendering • 真實的 DOM 節點 • 想要測試與 DOM 的互動與後續產⽣生的變化(如 Click 後刪除了了⼀一個 list 的項⽬目) • 想要測試完整的⽣生命週期

Slide 32

Slide 32 text

語法與規則

Slide 33

Slide 33 text

describe & it 測試腳本裡⾯面應該包括⼀一個或多個describe塊,每個describe塊應 該包括⼀一個或多個it塊。 describe塊稱為"測試套件"(test suite),表⽰示⼀一組相關的測試。 它是⼀一個函數,第⼀一個參參數是測試套件的名稱("加法函數的測試 "),第⼆二個參參數是⼀一個實際執⾏行行的函數。 it塊稱為"測試⽤用例例"(test case),表⽰示⼀一個單獨的測試,是測試的 最⼩小單位。它也是⼀一個函數,第⼀一個參參數是測試⽤用例例的名稱("1 加 1 應該等於 2"),第⼆二個參參數是⼀一個實際執⾏行行的函數。 http://www.ruanyifeng.com/blog/2015/12/a-mocha-tutorial-of-examples.html

Slide 34

Slide 34 text

describe & it http://www.ruanyifeng.com/blog/2015/12/a-mocha-tutorial-of-examples.html import add from './add'; import { expect } from 'chai'; describe('加法函數的測試', function () { it('1 加 1 應該等於 2', function () { expect(add(1, 1)).to.be.equal(2); }); });

Slide 35

Slide 35 text

expect 所謂"斷⾔言",就是判斷源碼的實際執⾏行行結果與預期結果 是否⼀一致,如果不⼀一致就拋出⼀一個錯誤。 http://www.ruanyifeng.com/blog/2015/12/a-mocha-tutorial-of-examples.html expect(add(1, 1)).to.be.equal(2);

Slide 36

Slide 36 text

Jest 提供的 API • describe, it • xdescribe, xit - 暫時跳過測試時使⽤用 • fit - 僅跑此段測試 • beforeEach - 在每個測試之前都執⾏行行 • afterEach - 在每個測試之後都執⾏行行 http://www.ruanyifeng.com/blog/2015/12/a-mocha-tutorial-of-examples.html https://facebook.github.io/jest/docs/api.html#content

Slide 37

Slide 37 text

Jest 提供的 API • expect • .toBe(value) • .not • .toBeTruthy(), .toBeFalsy() • .toEqual(value) • .toBeCalled() • .toBeCalledWith(arg1, arg2, …) http://www.ruanyifeng.com/blog/2015/12/a-mocha-tutorial-of-examples.html https://facebook.github.io/jest/docs/expect.html#content

Slide 38

Slide 38 text

專案使⽤用的測試檔案命名規則 (1/2) • 欲測試的 source 同層開⼀一個 __tests__ ⽬目錄 • 檔名:{source 檔名}.test.js • 另外,因⽬目前使⽤用 Ducks: Redux Reducer Bundles 的規範撰 寫 redux,故 reducers 與 actions 的命名規範如下: • Actions: {source 檔名}.actions.test.js • Reducers: {source 檔名}.reducer.test.js

Slide 39

Slide 39 text

專案使⽤用的測試檔案命名規則 (2/2) • 若若需定義很長的「測試⽤用資料」,建議另外建⼀一⽀支檔 案放置,並放置在同⼀一層。再於測試檔案中 import 進來來使⽤用。 • 會放在 __tests__/mocks/ 底下,例例如: • mockNews.json • 可直接抓回 API 的 response 當做內容

Slide 40

Slide 40 text

範例例專案

Slide 41

Slide 41 text

https://github.com/patw0929/jest-example-for-study-group 試做⼀一個「前端找⼯工作」的微型專案, 並加上 unit test!

Slide 42

Slide 42 text

各部分 Test 重點與⽅方法 (以 react + redux 為例例)

Slide 43

Slide 43 text

Component 測試重點 1. Snapshot 測試 (Jest v14.0 出現) • 比對產⽣生的結果是否與上次快照相符 • https://facebook.github.io/jest/blog/2016/07/27/jest-14.html/ • https://www.youtube.com/watch?v=HAuXJVI_bUs 2. Simulate ⾏行行為測試(例例如 click 之後改變了了 state) 因此快照也要進 git 在測 reducer 也可以⽤用喔!!

Slide 44

Slide 44 text

Component - snapshot snapshot

Slide 45

Slide 45 text

Component - snapshot snapshot + enzyme-to-json

Slide 46

Slide 46 text

Component - simulate - click 想測試點擊後的⾏行行為是否如預期

Slide 47

Slide 47 text

Component - simulate - click 模擬點擊 > 觀察結果是否如預期

Slide 48

Slide 48 text

Container 測試重點 1. Container 與其 Component 都有被 render 出來來 • 需⽤用 包起來來 2. 被 Mock 的 Component 有被 call ⼀一次 3. 呼叫 action 後,dispatch 有被執⾏行行

Slide 49

Slide 49 text

Container 準備假資料、⽤用 Provider 包起來來、傳入 context(如果有)

Slide 50

Slide 50 text

Container ⾃自⼰己 mock createStore 這樣就可以測試 dispatch 有沒有被執⾏行行

Slide 51

Slide 51 text

Container Component 有被 call ⼀一次 有 render 結果

Slide 52

Slide 52 text

Container 執⾏行行 action 後,dispatch 亦有被執⾏行行

Slide 53

Slide 53 text

Reducer 測試重點 1. 輸入 initial state 與 action 後,返回預期的 state 2. 連續輸入 state 與 action 後,與 snapshots 相符

Slide 54

Slide 54 text

Reducer

Slide 55

Slide 55 text

Reducer - Snapshot test https://twitter.com/villeimmonen/status/772754403187126272

Slide 56

Slide 56 text

snapshot ⽤用於 reducer

Slide 57

Slide 57 text

snapshot ⽤用於 reducer

Slide 58

Slide 58 text

Action 測試重點 1. 執⾏行行 action 後,返回預期的 object (type 與 payloads) 2. 承上,但中間經過 mock 過的 ajax

Slide 59

Slide 59 text

Action

Slide 60

Slide 60 text

Action - mock ajax (1/4) • nock • mock the HTTP requests
 • redux-mock-store • apply the middleware to a mock store http://redux.js.org/docs/recipes/WritingTests.html#async-action-creators

Slide 61

Slide 61 text

Action - mock ajax (2/4)

Slide 62

Slide 62 text

Action - mock ajax (3/4)

Slide 63

Slide 63 text

Action - mock ajax (4/4) 把原本 ajax 的部份 mock 掉 也可以⽤用 ES7 await/async

Slide 64

Slide 64 text

其他 jest ⼩小技巧

Slide 65

Slide 65 text

jest.mock(module_path, fn) (1/2) • Jest 特⾊色之⼀一,讓測試專注在現在測試的模組本⾝身, import 的 module mock 掉,避免影響測試中的模 組。 • 但 Jest v15 起,automocking 預設是關掉的。(官 ⽅方說明)

Slide 66

Slide 66 text

jest.mock(module_path, fn) (2/2)

Slide 67

Slide 67 text

__mocks__ 事先準備 mocked 版本的程式碼: 可⽤用在測試與真實環境下的程式碼有所差異異的時候 ⚠ 在測試中還是記得要 jest.mock(路路徑) 哦!

Slide 68

Slide 68 text

jest.resetModules() 確保 import/require 的 module 沒有被污染。 通常放在 beforeEach() 中:

Slide 69

Slide 69 text

jest.fn() (1/2) 把第三⽅方或傳入的 function mock 起來來,如此僅要測試 被 call 的次數或是附帶傳入的參參數是否正確即可。 例例:在程式碼中呼叫了了 ga/piwik 的程式碼,但測試時沒有必要
 載入 Google Analytics,只要 jest.fn() mock 起來來就好。

Slide 70

Slide 70 text

jest.fn() (2/2) 測試可以測該 mock 過的 function 是否被 call?
 被 call 的時候附帶什什麼參參數? 在 beforeEach 中⽤用 mockFn.mockClear() 確保每次 都重置狀狀態(傳入參參數、次數)。

Slide 71

Slide 71 text

timer 相關 mock (1/3) 針對 timer 也可以 mock,jest.useFakeTimers() 原⽣生的⽅方法 (setTimeout, setInterval, clearTimeout, clearInterval) 就真的要跑那麼 久,⽤用 Jest 快轉吧! https://facebook.github.io/jest/docs/timer-mocks.html#content

Slide 72

Slide 72 text

timer 相關 mock (2/3) https://facebook.github.io/jest/docs/timer-mocks.html#content

Slide 73

Slide 73 text

timer 相關 mock (3/3) https://facebook.github.io/jest/docs/timer-mocks.html#content

Slide 74

Slide 74 text

Mockdate 有時候會⽤用到 new Date(),但執⾏行行時是當下的時間。 (造成每次 snapshot 都跟上次不⼀一樣) 那就⽤用 mockdate.set(指定時間) 吧! https://github.com/boblauer/MockDate

Slide 75

Slide 75 text

CI Server 的時區不⼀一樣 加上 TZ=Asia/Taipei 指定 TimeZone

Slide 76

Slide 76 text

Async Test Async/Await Error Handling https://facebook.github.io/jest/docs/tutorial-async.html#content

Slide 77

Slide 77 text

Coverage

Slide 78

Slide 78 text

Coverage jest 內建 coverage。我們也在 package.json 設定好 script 了了。 會根據 package.json 中 collectCoverageFrom 設定的⽬目錄範圍計算 coverage。
 產⽣生這樣的結果:

Slide 79

Slide 79 text

Coverage 也會在 coverage/ ⽬目錄(預設)中產⽣生 HTML 版的報告:

Slide 80

Slide 80 text

Codecov

Slide 81

Slide 81 text

Codecov

Slide 82

Slide 82 text

Travis + Coveralls .travis.yml

Slide 83

Slide 83 text

參參考資料

Slide 84

Slide 84 text

參參考資料 • Give Jest Another Go
 https://medium.com/@jrwebdev/give-jest-another-go-86ca3d00f75 • Unit Test Redux Container Components - Part 1
 http://www.wsbrunson.com/react/redux/test/2016/05/08/testing-redux-containers.html • React.js 101 - React 測試入⾨門教學
 https://github.com/kdchang/reactjs101/tree/master/Appendix03 • Testing React Components With Enzyme
 http://codeheaven.io/testing-react-components-with-enzyme/ • Introduction to Jest testing framework (Youtube video)
 https://www.youtube.com/watch?v=tvy0bSgwtTo • Testing Your React (and redux) Applications (Youtube video)
 https://www.youtube.com/watch?v=n49QKIzS_Ik • 前端測試⼯工具簡單整理理和比較
 https://github.com/ellisMing/introduction-angular-test#前端測試⼯工具簡單整理理和比較

Slide 85

Slide 85 text

Thanks! <(_ _)>