之前,寫完一段代碼我也是直接調用或者實例化一下,發現過了就把測試相關部分刪了。今年的不幸與坎坷使我有很長一段時間去思考人生,不想將就了,魯棒健壯的程序,開發和測試應該是分得很開的,因而我選擇jest去作單元測試這件事。javascript
在開始以前,咱們先思考這樣一個問題,咱們爲何要作單元測試?html
不扯犢子直接說吧,第一點,用數據、用茫茫多的測試用例去告訴使用者,你的程序是多麼魯棒健壯;第二點,把它做爲一種素養去培養吧,當你按照一系列規範去作事,那麼你作出來的東西,我想是有品質在的。java
在確保你的電腦裝有node環境的狀況下,咱們經過mkdir jest-study && cd jest-study
來初始化項目,而後咱們經過npm init -y
初始化npm環境。node
執行npm i jest babel-jest @babel/core @babel/preset-env
命令安裝相應的依賴包,由於後面的例子是基於ES Module的語法編寫的,全部須要安裝babel進行語法轉義。固然你也能夠選擇直接用CommonJS的寫法,node自然支持的。git
這裏筆者羅列了經常使用的通用的一些關於jest的腳本,後面測試結果會陸續補充一些測試腳本,以上的腳本都編寫在package.json
文件下的scripts
腳本下面。github
"test": "jest"
: 這個比較傻瓜式,當執行npm run test
這條命令是會去對test
目錄下的全部文件進行相應的jest測試。npm
"test:help": "jest --help"
: 顧名思義,若是你不想全局安裝jest,又想看看到底有哪些cli命令的話,就它了。json
"test:debug": "jest --debug"
: 顧名思義,debug啊。promise
"test:verbose": "jest --verbose"
: 以層級顯示地方式在控制檯展現測試結果。緩存
"test:noCache": "jest --no-cache"
: 顧名思義,就是設置有沒有緩存,有緩存的話會快點。
"test:init": "jest --init"
: 執行這句就是在根目錄建立一個jest.config.js
文件,它在建立的時候有不少選擇項給你的。
"test:caculator": "jest ./test/caculator.test.js"
: 單文件測試。
"test:caculator:watch": "jest ./test/caculator.test.js --watch"
: 單文件監視測試
"test:watchAll": "jest --watchAll"
: 監視全部文件改動,測試相應的測試。
大體基礎類的腳本測試就總結到這裏,接下來咱們看下jest.config.js
的相關配置。
裏面配置的參數太多了,有些配置了之後就能夠再也不package.json
文件下寫相應的腳本,這裏筆者閹割一部分,列舉最多見的幾個。
module.exports = { // Automatically clear mock calls and instances between every test clearMocks: true, // The directory where Jest should output its coverage files coverageDirectory: "coverage", // The test environment that will be used for testing testEnvironment: "node", }
{ "presets": [["@babel/preset-env", { "targets": { "node": "current" } } ]] }
這裏就是配置了相應的語法轉換預設,若是後期有其餘需求,能夠經過plugins
去配置寫補丁轉義器,相關內容這裏就不作介紹了,能夠看下筆者以前寫的關於babel的文章。
考慮到把相關信息打在控制檯上,第一,控制檯可能會出現一處的狀況;第二,在查看結果內容多的話可能引發眼睛不適,全部就有了樓下幾種可能。
在package.json
中的scripts
下配置 "test:coverage": "jest --coverage"
後,而後執行相應腳本,就會在根目錄輸出一個coverage
文件夾,裏面包含了相應的測試腳本。固然控制檯也會輸出的。
執行 npm i jest-html-reporter
安裝這個模塊包(這裏說起一下,在npm版本大於5.x之後,能夠默認不加--save這種參數),而後在jest.config.js
中配置以下:
reporters: [ "default", ["./node_modules/jest-html-reporter", { "pageTitle": "Test Report" }] ],
執行相關的jest測試後,會在根目錄生成一個test-report.html
文件,打開後形如:
在package.json
中配置scripts
腳本 "test:exportJson": "jest --json --outputFile=./export/reporter.json"
,而後執行npm run test:exportJson
就會輸出相應的json報告文件,控制檯也會以json的形式輸出相應信息。
斷言庫的種類有不少,例如、assert、should、expect、chai等等,樓下的例子,筆者均以expect做爲講解。
先說個最簡單的expect(received).not.toBe(expected)
,這句話的意思就是表示否對,表示我斷言、接收值不等於指望值。
這個API經常使用於斷言,值類型的指望值,也就是boolean、string、number、這些類型的,用它作引用類型的斷言是不合適也不可取的。
to_be.test.js
describe('#toBe', () => { it('to be string', () => { expect('hello world').toBe('hello world') }) it('to be number', () => { expect(1 + 1).toBe(2) }) it('to be boolean', () => { expect(true).toBe(true) expect(false).toBe(false) }) it('to be null', () => { expect(null).toBe(null) }) it('to be undefined', () => { expect(undefined).toBe(undefined) }) })
通俗的理解就是等於, 能夠是值類型也能夠是引用類型的相等。
to_equal.test.js
test('#toEqual', () => { expect('hello world').toEqual('hello world') expect(110).toEqual(110) expect(true).toEqual(true) expect(false).toEqual(false) expect(null).toEqual(null) expect(undefined).toEqual(undefined) expect([1, 2, 3, 4]).toEqual([1, 2, 3, 4]) expect({ name: 'ataola' }).toEqual({ name: 'ataola' }) })
toContain()
跟的指望值是值類型的,而toContainEqual()
`能夠是值類型也能夠是引用類型,表示包含。
to_contain.test.js
test('#toContain', () => { expect([1, 2, 3, 4]).toContain(1) expect([[1, 2], [3, 4], [5, 6]]).toContainEqual([1, 2]) })
樓下expect後面跟的英語的字面量意思就是其方法的做用,分別是,大於、大於等於、小於、小於等於、類似於(接近於),這裏值得一題的事最後一個toBeCloseTo()
,思考一下改爲toBe()
能夠嗎?很顯然不行,其算出來的結果是0.30000000000000004
,究其緣由是js採用的是雙精度浮點表示。
number_compare.test.js
test('number compare', () => { expect(3).toBeGreaterThan(2) expect(3).toBeGreaterThanOrEqual(2.5) expect(3).toBeLessThan(4) expect(3).toBeLessThanOrEqual(3.5) expect(0.1 + 0.2).toBeCloseTo(0.3) // <0.05 passed })
顧名思義,字符串匹配,它支持字符串和正則,/^(\w+)\1+$/
匹配的是一個字符串能夠由其字串經過n次組合而成的字串(leetcode一道題目),全部其匹配到的是tao
。
string_match.test.js
test('string match', () => { expect('ataola').toMatch('ataola') expect('ataola').not.toMatch('aloata') expect('taotao'.match(/^(\w+)\1+$/)[0]).toMatch('tao') })
null、undefined、真假值比較特殊,全部這裏單獨有個方法表示它們。
truthiness.test.js
// toBeNull、 toBeUndefined 、 toBeDefined 、 toBeTruthy、 toBeFalsy test('truthiness', () => { expect(null).toBeNull() expect(undefined).toBeUndefined() expect('i am defined').toBeDefined() expect(true).toBeTruthy() expect(false).toBeFalsy() })
這裏是處理相關異常的, 後面能夠什麼都不根,也能夠跟個Error,或者相應的Error輸出信息
exceptions.test.js
function gaoError() { throw new Error('二營長開炮,開炮,開炮。。。') } test('#ToThrow', () => { expect(gaoError).toThrow() expect(gaoError).toThrow(Error) expect(gaoError).toThrow('二營長開炮,開炮,開炮。。。') })
好了,到這裏比較基礎和通用的API就介紹到這裏。接下來,咱們經過本身編寫相關代碼去鞏固下樓上的知識,這裏筆者提供兩個demo,一個是關於異步獲取數據的斷言、一個是實現一個計算器類的斷言。
咱們經過request-promise
這個庫去請求https://v1.hitokoto.cn
去獲取相應的json數據,而後進行斷言。
hitokoto.js
import rp from 'request-promise' const getHitokoto = async () => { const res = await rp('https://v1.hitokoto.cn') return res } export default getHitokoto
hitokoto.test.js
import getHitokoto from '../src/hitokoto' test('hitokoto', async () => { const data = await getHitokoto() expect(data).not.toBeNull() })
這裏就意思下,讀者能夠把data裏面的數據解構出來,進行相應的斷言。
這裏模擬了筆者手機上的計算器,實現了加減乘除清零計算等功能。
caculator.js
class CaCulator { constructor() { this.result = 0 } add(...args) { let { result } = this result += args.reduce((pre, cur) => pre + cur) this.result = result return this } reduce(...args) { let { result } = this result -= args.reduce((pre, cur) => pre + cur) this.result = result return this } multiply(...args) { let { result } = this if (result) { for (const val of args) { result *= val } } this.result = result return this } divide(...args) { let { result } = this const has_zero = args.some(item => item === 0) if (has_zero) { result = '數學體育老師教的嗎?' } else { for (const val of args) { result /= val } } this.result = result return this } clear() { this.result = 0 return this } exec() { const { result } = this if (typeof result === 'string') { this.result = 0 } return result } init(n) { this.result = typeof n === 'number' ? n : 0 return this } } export default CaCulator
caculator.test.js
import Caculator from '../src/caculator' const caculator = new Caculator() describe('test Caculator', () => { test('#add', () => { expect(caculator.add(1).exec()).toBe(1) expect(caculator.clear().add(1, 2, 3).exec()).toBe(6) caculator.clear() }) test('#reduce', () => { expect(caculator.reduce(1).exec()).toBe(-1) expect(caculator.clear().reduce(1, 2, 3).exec()).toBe(-6) caculator.clear() }) test('#multiply', () => { expect(caculator.multiply(1).exec()).toBe(0) expect(caculator.init(1).multiply(2, 3, 4, 5).exec()).toBe(120) caculator.clear() }) test('#divied', () => { expect(caculator.divide(0).exec()).toBe('數學體育老師教的嗎?') expect(caculator.divide(1, 2).exec()).toBe(0) expect(caculator.init(100).divide(2, 2).exec()).toBe(25) }) })
這裏筆者只是羅列了平常開發中經常使用的斷言API,具體的仍是要參見官方文檔這樣的一手資料,但願能起到拋磚引玉的效果。
https://github.com/Hargne/jest-html-reporter#readme
https://jestjs.io/docs/en/configuration
https://jestjs.io/docs/en/expect
https://jestjs.io/docs/en/using-matchers
本做品採用知識共享署名-非商業性使用-相同方式共享 4.0 國際許可協議進行許可。