目前開發大型應用,測試是一個非常重要的環節,但是大多數前端開發者對測試相關的知識是比較缺乏的。因爲可能項目開發週期短根本沒有機會寫,所以你沒有辦法體會到前端自動化測試的重要性。
來說說爲什麼前端自動化測試如此重要!
先看看前端常見的問題:
- 修改某個模塊功能時,其它模塊也受影響,很難快速定位bug
- 多人開發代碼越來越難以維護
- 不方便迭代,代碼無法重構
- 代碼質量差
增加自動化測試後:
- 我們爲核心功能編寫測試後可以保障項目的可靠性
- 強迫開發者編寫更容易被測試的代碼,提高代碼質量
- 編寫的測試有文檔的作用,方便維護
1.測試簡介
1.1 黑盒測試和白盒測試
- 黑盒測試一般也被稱爲功能測試,黑盒測試要求測試人員將程序看作一個整體,不考慮其內部結構和特性,只是按照期望驗證程序是否能正常工作
- 白盒測試是基於代碼本身的測試,一般指對代碼邏輯結構的測試。
1.2 測試分類
單元測試(Unit Testing)
單元測試是指對程序中最小可測試單元進行的測試,例如測試一個函數
、一個模塊
、一個組件
...
集成測試(Integration Testing)
將已測試過的單元測試函數進行組合集成暴露出的高層函數或類的封裝,對這些函數或類進行的測試
端到端測試(E2E Testing)
打開應用程序模擬輸入,檢查功能以及界面是否正確
1.3 TDD & BDD
TDD是測試驅動開發(Test-Driven Development)
TDD的原理是在開發功能代碼之前,先編寫單元測試用例代碼
BDD是行爲驅動開發(Behavior-Driven Development)
系統業務專家、開發者、測試人員一起合作,分析軟件的需求,然後將這些需求寫成一個個的故事。開發者負責填充這些故事的內容,保證程序實現效果與用戶需求一致。
小結:
TDD是先寫測試再開發 (一般都是單元測試,白盒測試);而BDD則是按照用戶的行爲來開發,再根據用戶的行爲編寫測試用例 (一般都是集成測試,黑盒測試)
1.4 測試框架
- Karma:Karma爲前端自動化測試提供了跨瀏覽器測試的能力,可以在瀏覽器中執行測試用例
- Mocha:前端自動化測試框架,需要配合其他庫一起使用,像chai、sinon...
- Jest:Jest 是Facebook推出的一款測試框架,集成了 Mocha,chai,jsdom,sinon等功能。
- ...
看到這裏Facebook
都在推Jest,你還不學嗎? Jest也有一些缺陷就是不能像Karma
這樣直接跑在瀏覽器上,它採用的是jsdom
,優勢是簡單、0配置! 後續我們通過Jest來聊聊前端自動化測試。
2.Jest的核心應用
在說Jest
測試之前,先來看看以前我們是怎樣測試的
const parser = (str) =>{
const obj = {};
str.replace(/([^&=]*)=([^&=]*)/g,function(){
obj[arguments[1]] = arguments[2];
});
return obj;
}
const stringify = (obj) =>{
const arr = [];
for(let key in obj){
arr.push(`${key}=${obj[key]}`);
}
return arr.join('&');
}
// console.log(parser('name=zf')); // {name:'zf'}
// console.log(stringify({name:'zf'})) // name=zf
我們每寫完一個功能,都先需要手動測試功能是否正常,測試後可能會將測試代碼註釋起來,這樣會產生一系列問題。因爲會污染源代碼,所有的測試代碼和源代碼混合在一起。如果刪除掉,下次測試還需要重新編寫。
所以測試框架就幫我們解決了上述的問題
2.1 分組、用例
Jest是基於模塊
的,我們需要將代碼包裝成模塊的方式,分別使用 export
將 parser
、stringify
這兩個方法導出。
安裝jest
npm init -y # 初始化pacakge.json
npm i jest
我們建立一個qs.test.js
來專門編寫測試用例,這裏的用例你可以認爲就是一條測試功能 (後綴要以.test.js結尾,這樣jest測試時默認會調用這個文件)。
import {parser,stringify} from './qs';
it('測試 parser 是否能正常解析結果',()=>{
// expect 斷言,判斷解析出來的結果是否和 {name:'zf'}相等
expect(parser(`name=zf`)).toEqual({name:'zf'});
})
jest
默認自帶斷言功能,斷言的意思就是判斷是不是這個樣子,我斷定你今天沒吃飯~,結果你吃了,說明這次斷言就失敗了,測試就無法通過。
通過配置scripts
來執行命令
"scripts": {
"test": "jest"
}
執行 npm run test
,可惜的是默認在node
環境下不支持es6模塊
的語法,需要babel
轉義,當然你也可以直接使用commonjs規範來導出方法,因爲大多數現在開發都採用es6模塊,所以安裝一下即可。
# core是babel的核心包 preset-env將es6轉化成es5
npm i @babel/core @babel/preset-env --save-dev
並且需要配置.babelrc
文件,告訴babel用什麼來轉義
{
"presets":[
[
"@babel/preset-env",{
"targets": {"node":"current"}
}
]
]
}
默認Jest中集成了babel-jest
,運行時默認會調用.babelrc
進行轉義,可以直接將es6轉成es5語法。
運行 npm run test
出現:
繼續編寫第二個用例
import {parser,stringify} from './qs';
describe('測試qs 庫',()=>{
it('測試 parser 是否能正常解析結果',()=>{
expect(parser(`name=zf`)).toEqual({name:'zf'});
});
it('測試 stringify 是否正常使用stringify',()=>{
expect(stringify({name:'zf'})).toEqual(`name=zf`)
})
});
describe的功能是給用例分組,這樣可以更好的給用例分類,其實這就是我們所謂的單元測試,對某個具體函數和功能進行測試。
2.2 matchers匹配器
在寫第一個測試用例時,我們一直在使用toEqual
其實這就是一個匹配器,那我們來看看jest
中常用的匹配器有哪些?因爲匹配器太多了,所以我就講些常用的!
爲了方便理解,我把匹配器分爲三類:判斷相等、不等、是否包含。
it('判斷是否相等',()=>{
expect(1+1).toBe(2); // 相當於 js中的===
expect({name:'zf'}).toEqual({name:'zf'}); // 比較內容是否相等
expect(true).toBeTruthy(); // 是否爲 true / false 也可以用toBe(true)
expect(false).toBeFalsy();
});
it('判斷不相等關係',()=>{
expect(1+1).not.toBe(3); // not取反
expect(1+1).toBeLessThan(5); // js中的小於
expect(1+1).toBeGreaterThan(1); // js中的大於
});
it('判斷是否包含',()=>{
expect('hello world').toContain('hello'); // 是否包含
expect('hello world').toMatch(/hello/); // 正則
});
2.3 測試操作節點方法
說了半天,我們自己來寫個功能測試一下!
export const removeNode = (node) => {
node.parentNode.removeChild(node)
};
核心就是測試傳入一個節點,這個節點是否能從DOM
中刪除
import { removeNode } from './dom'
it('測試刪除節點',()=>{
document.body.innerHTML = `<div><button data-btn="btn"></button</div>`
let btn = document.querySelector('[data-btn="btn"]')
expect(btn).not.toBeNull()
removeNode(btn);
btn = document.querySelector('[data-btn="btn"]');
expect(btn).toBeNull()
})
這個就是我們所說的jsdom
,在node中操作DOM元素
2.4 Jest常用命令
我們希望每次更改測試後,自動重新執行測試,修改執行命令:
"scripts": {
"test": "jest --watchAll"
}
重新執行 npm run test
,這時就會監控用戶的修改
提示我們按下w
,顯示更多信息
這裏我把每個命令的含義都列好了,有需要可以自己嘗試一下~
想要知道如何測試異步邏輯,如何mock接口數據,如何深度使用Jest嗎?敬請期待下期文章!