當前時代,前端技術的飛速發展與更迭,前端框架的百花齊放,前端從業人員激增,做爲一個有素養,有理想的前端童鞋如何保證本身的代碼質量,讓本身的代碼更加嚴謹、可靠成了咱們繞不過去的話題,這時候就離不開測試,固然做爲一個開發人員的話咱們更注重的應該是單元測試。javascript
ATDD,TDD,BDD
聊到了測試,那咱們先簡單提幾個概念:ATDD,TDD,BDD.html
ATDD: Acceptance Test Driven Development(驗收測試驅動開發):前端
這是一種在編碼開始以前將客戶帶入測試設計過程的技術。它也是一個協做實踐,用戶,測試人員和開發人員定義了自動驗收標準。若是系統未經過測試可提供快速反饋,說明未知足要求。驗收測試以業務領域術語進行指定。每一個功能都必須提供真實且可衡量的業務價值。java
TDD: Test-driven development (測試驅動開發):ios
是一種使用自動化單元測試來推進軟件設計並強制依賴關係解耦的技術。在編寫真正實現功能的代碼以前先編寫測試,每次測試以後,重構完成,而後再次執行相同或相似的測試。該過程根據須要重複屢次,直到每一個單元根據所需的規格運行。正則表達式
BDD:Behavior-Driven Development (行爲驅動開發):npm
是一種敏捷軟件開發的技術,它鼓勵軟件項目中的開發者、QA和非技術人員或商業參與者之間的協做。json
使用BDD的團隊應該可以以用戶故事的形式提供大量的「功能文檔」,並增長可執行場景或示例。 BDD一般有助於領域專家理解實現而不是暴露代碼級別測試。它一般以GWT格式定義:GIVEN WHEN&THEN。axios
測試工具的類型
組合使用工具很常見,即便已選框架也能實現相似的功能數組
- 提供測試框架(Mocha, Jasmine, Jest, Cucumber)
- 提供斷言(Chai, Jasmine, Jest, Unexpected)
- 生成,展現測試結果(Mocha, Jasmine, Jest, Karma)
- 快照測試(Jest, Ava)
- 提供仿真(Sinon, Jasmine, enzyme, Jest, testdouble)
- 生成測試覆蓋率報告(Istanbul, Jest, Blanket)
- 提供類瀏覽器環境(Protractor, Nightwatch, Phantom, Casper)
不一樣測試框架的對比
Jest
- facebook 坐莊
- 基於 Jasmine 至今已經作了大量修改添加了不少特性
- 開箱即用配置少,API簡單
- 支持斷言和仿真
- 支持快照測試
- 在隔離環境下測試
- 互動模式選擇要測試的模塊
- 優雅的測試覆蓋率報告,基於Istanbul
- 智能並行測試
- 較新,社區不十分紅熟
- 全局環境,好比 describe 不須要引入直接用
- 較多用於 React 項目(但普遍支持各類項目)
Mocha
- 靈活(不包括斷言和仿真,本身選對應工具) 流行的選擇:chai,sinon
- 社區成熟用的人多,測試各類東西社區都有示例
- 須要較多配置
- 可使用快照測試,但依然須要額外配置
Jasmine
- 開箱即用(支持斷言和仿真)
- 全局環境
- 比較'老',坑基本都有人踩過了
AVA
- 異步,性能好
- 簡約,清晰
- 快照測試和斷言須要三方支持
Tape
- 體積最小,只提供最關鍵的東西
- 對比其餘框架,只提供最底層的 API
總結一下,Mocha 用的人最多,社區最成熟,靈活,可配置性強易拓展,Jest 開箱即用,裏邊啥都有提供全面的方案,Tape 最精簡,提供最基礎的東西最底層的API。
下面開始今天主要的話題,前端單元測試jest.js的學習使用
Jest 做爲 Facebook 的一套開源的 JavaScript 測試框架, 它自動集成了斷言、JSDom、覆蓋率報告等開發者所須要的全部測試工具,是一款幾乎零配置的測試框架。
單元測試主要思想與步驟
-
編寫測試用例
即描述單元測試要作什麼,給定一個輸入,定義指望的輸出,檢驗要測試的代碼是否符合指望
-
編寫被測試代碼
-
執行測試,若是未經過測試,則修改被測試代碼直到測試經過
初始化與安裝
// 新建個項目並初始化 mkdir study-jest && cd npm init -y // 下載安裝jest npm i jest -D // 配置script腳本,以便經過命令行運行測試 "scripts": { "jest-t": "jest" } npm install -D babel-jest babel-core babel-preset-env regenerator-runtime /* babel-jest、 babel-core、 regenerator-runtime、babel-preset-env這幾個依賴是爲了讓咱們能夠 使用ES6的語法特性進行單元測試,ES6提供的 import 來導入模塊的方式,Jest自己是不支持的。*/ // 配置.babelrc文件 { "presets": ["env"] }
first blood
默認狀況下,咱們的測試文件通常放在項目中的tests
文件夾中,而且咱們測試文件命名通常是xx.spec.js
,因此建立一個tests
文件夾,並在裏邊建立一個名爲firstTest.spec.js
的文件來編寫咱們的第一個測試用例
如今咱們要實現一個sum函數用來作整數的相加,首先咱們來編寫他的測試用例
// firstTest.spec.js // 描述要測試的模塊 test('sum(1 + 1) 等於 2', () => { // 斷言:即指望運行結果 expect(functions.sum(1, 1)).toBe(2); });
編寫被測試代碼
// functions.js export default { sum(a, b) { // return a; // 錯誤邏輯 return a + b; } }
編寫完成以後執行測試指令npm run jest-t
測試成功輸出
測試失敗輸出
經常使用的幾個Jest斷言
上面測試用例中的expect(functions.sum(2, 2)).toBe(4)
爲一句斷言,Jest爲咱們提供了expect
函數用來包裝被測試的方法並返回一個對象,該對象中包含一系列的匹配器來讓咱們更方便的進行斷言,上面的toBe
函數即爲一個匹配器。咱們來介紹幾種經常使用的Jest斷言,其中會涉及多個匹配器。
.not
// firstTest.spec.js // .not修飾符容許你測試結果不等於某個值的狀況 test('sum(1 + 1) 不等於 3', () => { expect(functions.sum(1, 1)).not.toBe(3); });
.toEqual()
// functions.js export default { getObj() { return { a: 1, b: 2 }; } };
// firstTest.spec.js /* .toEqual匹配器會遞歸的檢查對象全部屬性和屬性值是否相等,因此若是要進行應用類型的比較 時,請使用.toEqual而不是.toBe */ test('getObj() 返回的對象深度相等', () => { expect(functions.getObj()).toEqual(functions.getObj()); }); test('getObj()返回的對象內存地址不一樣', () => { expect(functions.getObj()).not.toBe(functions.getObj()); });
.toHaveLength
// functions.js export default { getIntArray(num) { if (!Number.isInteger(num)) { throw Error('"getIntArray"只接受整數類型的參數'); } let result = []; for (let i = 0, len = num; i < len; i++) { result.push(i); } return result; }; }
// firstTest.spec.js // .toHaveLength能夠很方便的用來測試字符串和數組類型的長度是否知足預期。 test('getIntArray(3)返回的數組長度應該爲3', () => { expect(functions.getIntArray(3)).toHaveLength(3); });
.toThrow
// firstTest.spec.js /* .toThrow可可以讓咱們測試被測試方法是否按照預期拋出異常,可是在使用時須要注意的是:咱們 必須使用一個函數將將被測試的函數作一個包裝,正如上面getIntArrayWrapFn所作的那樣,不然會 由於函數拋出致使該斷言失敗。*/ test('getIntArray(3.3)應該拋出錯誤', () => { function getIntArrayWrapFn() { functions.getIntArray(3.3); } expect(getIntArrayWrapFn).toThrow('"getIntArray"只接受整數類型的參數'); });
.toMatch
// firstTest.spec.js // .toMatch傳入一個正則表達式,它容許咱們用來進行字符串類型的正則匹配。 test('getObj().a應該爲數字', () => { expect(functions.getObj().a).toMatch(/\d/i); });
double kill
異步函數測試
這裏咱們使用最經常使用的http請求庫axios
來進行請求處理
npm install axios
編寫http請求函數 咱們將請求http://jsonplaceholder.typicode.com/users/1
,這是由JSONPlaceholder提供的mock請求地址
// asyncFn.js import axios from 'axios'; export default { fetchUser() { return axios.get('http://jsonplaceholder.typicode.com/users/1') .then(res => res.data) .catch(error => console.log(error)); } }
// secondTest.js test('fetchUser() 能夠請求到一個含有name屬性值爲Leanne Graham的對象', () => { expect.assertions(1); return asyncFn.fetchUser() .then(data => { expect(data.name).toBe('Leanne Graham'); }); }); /* 上面咱們調用了expect.assertions(1),它能確保在異步的測試用例中,有一個斷言會在回調函數中 被執行。這在進行異步代碼的測試中十分有效。*/
代碼覆蓋率
代碼覆蓋率指的是咱們寫的代碼有沒有單元測試所覆蓋到。Jest
具備內置的代碼覆蓋範圍,你能夠經過兩種方式激活它:
- 經過命令行傳遞標誌
--coverage
- 在
package.json
中配置Jest
// 方式一:命令行 npm run jest-t -- --coverage // 方式二:配置package.json中的jest "jest": { "collectCoverage": true } // 方式三:配置package.json中的script指令 "scripts": { "jest-t": "jest --coverage" },
測試完畢報告
咱們每次運行完npm run jest-t
時能夠在項目中看到一個名爲coverage
的文件夾,你能夠經過訪問coverage/lcov-report/index.html
來查看詳細的測試報告他能夠明確的指出未經測試的代碼。 詳細報告
參考資料
【1】 Jest官方文檔(https://jestjs.io/zh-Hans/)
【2】李棠輝-簡書(https://www.jianshu.com/p/70a4f026a0f1)