## 關於那個看起來很難的<font color=#ff7738>測試</font> --- ## <font color=#ff7738>前端測試</font>是什麼? 其實在前端的測試可以分成<font color=#ff7738>三</font>個種類:<!-- .element: class="fragment" data-fragment-index="1" --> <font>E2E Testing、</font><!-- .element: class="fragment" data-fragment-index="2" --><font>Integration Testing、</font><!-- .element: class="fragment" data-fragment-index="3" --><font>Unit Testing</font><!-- .element: class="fragment" data-fragment-index="4" --> ![](https://martinfowler.com/bliki/images/testPyramid/test-pyramid.png =60%x)<!-- .element: class="fragment" data-fragment-index="5" --> <font size=4>圖片來源:[TestPyramid 測試金字塔](https://martinfowler.com/bliki/TestPyramid.html)</font><!-- .element: class="fragment" data-fragment-index="5" --> --- #### <font color=#ff7738>避免成為冰淇淋甜筒</font> ![](https://pic.pimg.tw/kojenchieh/1546345783-4139256070.png =60%x)<!-- .element: class="fragment" data-fragment-index="1" --> <font size=4>圖片來源:[讓我們聊聊測試自動化金字塔](https://kojenchieh.pixnet.net/blog/post/466703174-%E8%AE%93%E6%88%91%E5%80%91%E8%81%8A%E8%81%8A%E6%B8%AC%E8%A9%A6%E8%87%AA%E5%8B%95%E5%8C%96%E9%87%91%E5%AD%97%E5%A1%94)</font><!-- .element: class="fragment" data-fragment-index="1" --> --- ## <font color=#ff7738>Unit</font> Testing 單元測試,擁有以下特點: 1. <font color=#ff7738>單點突破很強</font>,最小的測試單位可以是一個 function<!-- .element: class="fragment" data-fragment-index="1" --> 2. 會<!-- .element: class="fragment" data-fragment-index="2" --><font color=#ff7738>隔離真實環境</font><!-- .element: class="fragment" data-fragment-index="2" -->,對某部分的功能做測試 3. 測試的範圍較小,<font color=#ff7738>容易</font>發現錯誤<!-- .element: class="fragment" data-fragment-index="3" --> 4. 測試速度<font color=#ff7738>最快</font>,維護成本<!-- .element: class="fragment" data-fragment-index="4" --><font color=#ff7738>最低</font><!-- .element: class="fragment" data-fragment-index="4" --> 5. <font color=#ff7738>任何時候</font>都可以導入測試,甚至是在寫程式之前<!-- .element: class="fragment" data-fragment-index="5" --> --- ## 但<font color=#ff7738>為什麼</font>我們要寫測試? - <font color=#ff7738>不曉得</font>程式在哪一次修改後會出現錯誤<!-- .element: class="fragment" data-fragment-index="1" --> - 更容易為程式做<!-- .element: class="fragment" data-fragment-index="2" --><font color=#ff7738>重構</font><!-- .element: class="fragment" data-fragment-index="2" --> - <font color=#ff7738>節省時間</font>,不需要手動點擊 UI<!-- .element: class="fragment" data-fragment-index="3" --> --- ### 做測試的原因可能有很多 ## 但<font color=#ff7738>目的</font>都與<font color=#ff7738>讓程式變得更好</font>有關<!-- .element: class="fragment" data-fragment-index="1" --> --- ## 和<font color=#ff7738>手動測試</font>有什麼不同? - 每一次的手動測試都是<font color=#ff7738>一次性的花費</font>,你需要一直做<font color=#ff7738>相同</font>的事情。<!-- .element: class="fragment" data-fragment-index="1" --> - 自動化測試是一項<font color=#ff7738>投資</font>,隨著時間越久,報酬率就越高。<!-- .element: class="fragment" data-fragment-index="2" --> --- ## 接下來 ## 開始<font color=#ff7738>單元測試</font>吧! --- ## 但是該<font color=#ff7738>怎麼開始</font>? 在測試之前...<!-- .element: class="fragment" data-fragment-index="1" --> 先來了解他的<font color=#ff7738>原理</font>!<!-- .element: class="fragment" data-fragment-index="1" --> --- #### 看看下方的 function ```javascript= const sum = (a, b) => a + b; const value = sum(1, 3); // 希望 value 是 4,是或不是都顯示對應的提示 ``` #### 用 <font color=#ff7738>if 判斷</font>吧!<!-- .element: class="fragment" data-fragment-index="1" --> ```javascript= if (value === 4) { console.log('正確!'); } else { console.log('錯誤!'); } ``` <!-- .element: class="fragment" data-fragment-index="1" --> --- #### 那些測試框架就只是<font color=#ff7738>把判斷包裝</font>起來 ```javascript= const expect = (actual, expected) => { if (actual === expected) { console.log('正確!'); } else { console.log('錯誤!'); } } expect(sum(1, 3), 4); ``` --- # 但是<font color=#ff7738>還不夠</font>! ### 如果要判斷 <font color=#ff7738>NaN</font> 怎麼辦?<!-- .element: class="fragment" data-fragment-index="1" --> ```javascript= console.log(NaN === NaN); // 比較起來會是 false ``` <!-- .element: class="fragment" data-fragment-index="2" --> --- ### 為了要因應<font color=#ff7738>各種狀況的驗證</font> ## 所以我們不能只有一種<!-- .element: class="fragment" data-fragment-index="1" --><font color=#ff7738>判斷方法</font><!-- .element: class="fragment" data-fragment-index="1" --> --- ### 將判斷相等的邏輯<font color=#ff7738>放到一個物件</font>中 ```javascript= const expect = (actual) => ({ toBe: (expected) => { if (actual === expected) { console.log('正確!'); } else { console.log('錯誤!'); } }, }); ``` --- ### 在該物件內<font color=#ff7738>加入判斷 NaN</font> 的方法 ```javascript= const expect = (actual) => ({ toBe: (expected) => { /* do smoething */ }, toBeNaN: () => { if (isNaN(actual)) { console.log('正確!'); } else { console.log('錯誤!'); } }, }); ``` --- #### 經過修改後測試會變成... ```javascript= const expect = (actual) => ({ toBe: (expected) => { /* do something */ }, toBeNaN: () => { /* do something */ }, }); /* expect 會回傳斷言庫,因此可以直接使用斷言庫內的 method */ expect(sum(1, 3)).toBe(4); // 正確! expect(sum(1, 3)).toBe(5); // 錯誤! expect(NaN).toBeNaN(); // 正確! ``` --- ## 但<font color=#ff7738>還是不夠</font>啊! ### 斷言的方式還有其他...<!-- .element: class="fragment" data-fragment-index="1" --> - 非同步執行<!-- .element: class="fragment" data-fragment-index="2" --> - 拋出錯誤<!-- .element: class="fragment" data-fragment-index="3" --> - 判斷 Undefined<!-- .element: class="fragment" data-fragment-index="4" --> - 判斷 Truthy、Falsy<!-- .element: class="fragment" data-fragment-index="5" --> - 判斷 Object、Array<!-- .element: class="fragment" data-fragment-index="6" --> <br /> ### 全都<font color=#ff7738>要自己做嗎</font>?<!-- .element: class="fragment" data-fragment-index="7" --> --- ## 不需要! ### 現在的你<font color=#ff7738>和剛才不同了</font>!<!-- .element: class="fragment" data-fragment-index="1" --> ### 因為你<font color=#ff7738>已經知道測試框架</font>背後<!-- .element: class="fragment" data-fragment-index="2" --><font color=#ff7738>做了什麼</font><!-- .element: class="fragment" data-fragment-index="2" --> --- ### 進入<font color=#ff7738>測試框架</font> ### 開始<font color=#ff7738>使用 Jest</font>!<!-- .element: class="fragment" data-fragment-index="1" --> ![](https://i.imgur.com/aTvA81i.png =70%x)<!-- .element: class="fragment" data-fragment-index="1" --> <font size=4>圖片來源:[Jest 官方網站](https://jestjs.io/)</font><!-- .element: class="fragment" data-fragment-index="1" --> --- #### <font color=#ff7738>Jest 的測試</font>怎麼寫? ```javascript= test('用 1 與 3 執行 sum 會回傳 4', () => { const expected = 4; const actual = sum(1, 3); expect(actual).toBe(expected); }); ``` <!-- .element: class="fragment" data-fragment-index="1" --> ### 除了 test 之外<!-- .element: class="fragment" data-fragment-index="2" --> ### 是不是<font color=#ff7738>一切都變得簡單了</font>?<!-- .element: class="fragment" data-fragment-index="2" --> --- ### 一個測試案例會有哪些部分? - <font color=#ff7738>好的命名</font>,必須能夠<font color=#ff7738>具體知道測試</font>的內容<!-- .element: class="fragment" data-fragment-index="1" --> - 測試目標?<!-- .element: class="fragment" data-fragment-index="2" --> - 做了什麼?<!-- .element: class="fragment" data-fragment-index="3" --> - 期望什麼?<!-- .element: class="fragment" data-fragment-index="4" --> <br /> - <font color=#ff7738>好的內容</font>,遵循<!-- .element: class="fragment" data-fragment-index="5" --> <font color=#ff7738>3A 原則</font><!-- .element: class="fragment" data-fragment-index="5" --> - <font color=#ff7738>Arrange</font> 準備<!-- .element: class="fragment" data-fragment-index="6" --> - <font color=#ff7738>Act</font> 執行<!-- .element: class="fragment" data-fragment-index="7" --> - <font color=#ff7738>Assert</font> 驗證<!-- .element: class="fragment" data-fragment-index="8" --> --- #### 看回剛剛的測試案例 ```javascript= // 測試了 sum 並用 1 與 3 執行,最後回傳了 4 test('用 1 與 3 執行 sum 會回傳 4', () => { // Arrange:準備好期望的結果值 const expected = 4; // Act:用 1 和 3 執行 sum const actual = sum(1, 3); // Assert:斷言階段 expect(actual).toBe(expected); }); ``` ### 他現在甚至可以<font color=#ff7738>成為 sum 的操作手冊</font>!<!-- .element: class="fragment" data-fragment-index="1" --> --- #### 再做更多吧! ### 假設現在有個<font color=#ff7738>類別</font>...<!-- .element: class="fragment" data-fragment-index="1" --> ```javascript= class TodoList { constructor() { this.todoList = []; } getList() { return this.todoList; } addTodo(todo) { this.todoList.push(todo); } } ``` <!-- .element: class="fragment" data-fragment-index="1" --> --- ### 就可以這樣子做 ```javascript= test('使用 TodoList 的 addTodo 加入的事項,可以從 getList 中取出', () => { // Arrange const todo = new TodoList(); const expected = ['first']; // Act todo.addTodo('first'); // Assert expect(todo.getList()).toEqual(expected) }) ``` --- ### 加入 <font color=#ff7738>UI</font> 呢? #### 看看這裡 👇 [Unit Test | 香草 JS 的單元測試 feat. @testing-library/dom](https://medium.com/starbugs/unit-test-%E9%A6%99%E8%8D%89-js-%E7%9A%84%E5%96%AE%E5%85%83%E6%B8%AC%E8%A9%A6-feat-testing-library-dom-f8539137bec9) --- # 謝謝大家! --- 參考文章: - <font size=4>[Frontend Testing Vs. Backend Testing: What's the Difference?](https://www.guru99.com/frontend-testing-vs-backend-testing.html)</font> - <font size=4>[Introduction to Front-End unit testing](https://dev.to/christopherkade/introduction-to-front-end-unit-testing-510n)</font> - <font size=4>[What should we test (ReactJS Components)](https://hackernoon.com/what-should-we-test-reactjs-components-647ded674928)</font> - <font size=4>[Challenging the Testing Pyramid](https://juristr.com/blog/2019/07/testing-cypress-intro/)</font> - <font size=4>[JavaScript 單元測試導引 — 進入 Jest 測試前,先嘗試自己 Build 一個吧](https://medium.com/@leo36094/%E5%9F%BA%E7%A4%8E-javascript-%E8%87%AA%E5%8B%95%E5%8C%96%E6%B8%AC%E8%A9%A6%E5%B0%8E%E5%BC%95-b00729418668)</font> ---
{"title":"關於那個看起來很難的測試","tags":"F2EUnit.tw","slideOptions":{"theme":"white"}}
    2080 views
   owned this note