React 之 jest 前端自动化测试
一. 自動(dòng)化測(cè)試簡(jiǎn)介
-
為什么要前端自動(dòng)化測(cè)試:
自動(dòng)化測(cè)試可以間接的提供代碼的測(cè)試,多人協(xié)作時(shí)相互之間未知邏輯的改動(dòng)等產(chǎn)生的未知或新問(wèn)題的預(yù)警。有效避免一些未考慮到及低級(jí)的錯(cuò)誤。 -
自動(dòng)化測(cè)試需要工作:
自動(dòng)化測(cè)試需要我們手動(dòng)編寫(xiě)測(cè)試代碼,當(dāng)部分邏輯發(fā)生改變時(shí),也需要同步更新我們的測(cè)試代碼。重一定的角度上它也間接的提高了開(kāi)發(fā)及維護(hù)成本。這點(diǎn)在實(shí)際開(kāi)發(fā)運(yùn)用中,大家根據(jù)實(shí)際項(xiàng)目情況來(lái)衡量。 -
前段測(cè)試工具概覽:
前端測(cè)試工具紛繁復(fù)雜,大致分為測(cè)試框架, 斷言庫(kù), 測(cè)試覆蓋率工具等。
測(cè)試框架
測(cè)試框架的作用是提供一些方便的語(yǔ)法來(lái)描述測(cè)試的用例,以及對(duì)用例進(jìn)行分組。
測(cè)試框架可分為兩種: TDD(測(cè)試驅(qū)動(dòng)開(kāi)發(fā))和 BDD(行為驅(qū)動(dòng)開(kāi)發(fā))。
常見(jiàn)的測(cè)試框架有 Jasmine, Mocha 及 接下來(lái)我們要介紹的 Jest
斷言庫(kù)
斷言庫(kù)主要提供語(yǔ)義化方法,用于對(duì)參與測(cè)試的值做各種各樣的判斷。 這些語(yǔ)義化方法會(huì)返回測(cè)試的結(jié)果,要么成功,要么失敗。
產(chǎn)概念的斷言庫(kù)有Should.js Chai.js 等
測(cè)試覆蓋率工具
用于統(tǒng)計(jì)測(cè)試用例對(duì)代碼的測(cè)試情況, 生成響應(yīng)的報(bào)表。 比如* istanbul *
- 關(guān)于Jest 測(cè)試框架概述
Jest 是Facebook 出品的一個(gè)測(cè)試框架, 其一大特點(diǎn)是內(nèi)置了常用的測(cè)試工具,比如:自帶斷言(expect), 測(cè)試覆蓋率工具(coverage),實(shí)現(xiàn)了開(kāi)箱即用等 。
Jest 可以利用其特有的快照測(cè)試功能, 通過(guò)比對(duì)UI代碼生成的快照文件, 實(shí)現(xiàn)對(duì)React等常見(jiàn)框架的自動(dòng)化測(cè)試。
此外,Jest 測(cè)試用例是并行執(zhí)行的, 而且只執(zhí)行發(fā)生改變的文件所對(duì)應(yīng)的測(cè)試,提升了測(cè)試速度。
二. Jest 的實(shí)踐
1. 環(huán)境搭建
這里我們主要研究jest的搭建所以,您可以通過(guò)官網(wǎng)安裝 Create React App 來(lái)搭建一個(gè)開(kāi)發(fā)環(huán)境。
接下來(lái)我們需要做如下事情:
-
安裝依賴包
npm i jest babel-jest -D -
添加jest.config.js 文件
- 創(chuàng)建測(cè)試代碼文件目錄
在根目錄下創(chuàng)建*tests* 文件夾用來(lái)存放測(cè)試腳本文件。
注: Jest 的測(cè)試腳本名形如 **.test.js 或 *.spec.js。 當(dāng)執(zhí)行npm run test 命令時(shí),
它會(huì)執(zhí)行當(dāng)前目錄下的所有 的 *.test.js 或 *.spec.js 文件, 完成測(cè)試。
- package.json中添加命令
- 添加對(duì)Jest 的 ES6+支持
因?yàn)閖est是基于Node 環(huán)境運(yùn)行。 Node默認(rèn)對(duì)ES6+語(yǔ)法不全支持。所以如果我們用到了ES6+語(yǔ)法,需要為其添加語(yǔ)法支持。
2. 用法
- 用例的表示
Jest 內(nèi)部使用了 Jasmin2 來(lái)進(jìn)行測(cè)試, 故其用例語(yǔ)法與 Jasmine相同。==test()==函數(shù)來(lái)描述一個(gè)測(cè)試用例。
執(zhí)行命令 npm run my-test 輸出結(jié)果如下:
單元測(cè)試的幾個(gè)指標(biāo):
%stmts 是語(yǔ)句覆蓋率(statement coverage):是不是每個(gè)語(yǔ)句都執(zhí)行了?
%Branch 分支覆蓋率(branch coverage):是不是每個(gè)if代碼塊都執(zhí)行了?
%Funcs 函數(shù)覆蓋率(function coverage):是不是每個(gè)函數(shù)都調(diào)用了?
%Lines 行覆蓋率(line coverage):是不是每一行都執(zhí)行了?
- UI 組件測(cè)試
擴(kuò)展
細(xì)心的同學(xué)應(yīng)該注意到了,這個(gè)實(shí)例中用到了==enzyme,Adapter ==。 這里簡(jiǎn)單說(shuō)下兩者的作用。
(1)Enzyme 簡(jiǎn)介 傳送門(mén)
(2)Adapter : 在使用 enzyme 時(shí),需要先適配React版本。
npm i enzyme-adapter-react-16 -D //使用 import Adapter from 'enzyme-adapter-react-16' Enzyme.configure({ adapter: new Adapter() })為了避免每次測(cè)試文件都這么寫(xiě), 可以在test目錄下新建一個(gè)配置文件:
import Enzyme from 'enzyme'; import Adapter from 'enzyme-adapter-react-16';Enzyme.configure({adapter: new Adapter(), });export default Enzyme;然后在測(cè)試文件的時(shí)候引入這個(gè)配置文件即可:
import React from 'react' import Enzyme from './config/Enzyme.config'; ................ const {shallow}=Enzymedescribe('Enzyme的淺渲染測(cè)試套件', function () {it('Example組件中按鈕的名字為text的值', function () {const name='按鈕名'let app = shallow(<Example text={name} />)assert.equal(app.find('button').text(),name)}) })3. Jest 之快照測(cè)試(Snapshot)
如果你的項(xiàng)目中還沒(méi)有任何測(cè)試用例,那么使用快照測(cè)試將是一個(gè)最快的基本保障。
如果你想確保你的一些公共組件(UI)不會(huì)被意外的被修改變化,那么快照測(cè)試是一個(gè)非常有用的工具。
它的基本思想是:在測(cè)試文件目錄下生成快照文件目錄“snapshots/**.test.js.snap” 。 每次執(zhí)行測(cè)試命令時(shí),都會(huì)與該目錄下的對(duì)應(yīng)快照文件進(jìn)行內(nèi)容比對(duì)。 如果兩個(gè)圖像(內(nèi)容)不匹配,則測(cè)試失敗。 除非您同步更新了快照為最新版本(即測(cè)試用例中承認(rèn)且同意修改更新快照內(nèi)容)。
執(zhí)行測(cè)試命令后測(cè)試結(jié)果如下:
生成的快照文件內(nèi)容如下:
當(dāng)某人不小心修改了我們的公共UI組件代碼后(注:測(cè)試用例沒(méi)有修改):
// src/Link.Snapshot.js........... ................ render() {return (<aclassName={this.state.class}href={ (this.props.page + 'udpate udpate !') || '#'} // 假設(shè)修改了此處, 對(duì)href 添加了自定義字符串 ‘udpate udpate !’。onMouseEnter={this._onMouseEnter}onMouseLeave={this._onMouseLeave}>{this.props.children}</a>);}.......................................再次執(zhí)行測(cè)試命令輸入結(jié)果如下:
Jest 快照測(cè)試通過(guò)比對(duì)上次的快照輸出文件內(nèi)容,發(fā)現(xiàn)不一致。 輸出測(cè)試失敗! 表示該UI組件被修改…
如果我們統(tǒng)一本次的修改, 那么可以通過(guò): npm run my-test – -u 命令來(lái)同意同步更新歷史快照文件。更新完成后,則測(cè)試提示通過(guò)。
注: 快照文件應(yīng)該與代碼更改一起提交,并作為代碼審查過(guò)程的一部分進(jìn)行審核。
Jest 使用 pretty-format 對(duì)快照文件進(jìn)行了處理,代碼會(huì)變成可閱讀的文件。
3. 常用API
- Jest 全局方法
Describe(name, fn) : 測(cè)試套件,一組相關(guān)的測(cè)試用例。第一個(gè)參數(shù)是測(cè)試套餐的描述,第二個(gè)參數(shù)是測(cè)試用例。
const my = {name : "fynn",age : 27 } describe("my info", ()=>{test("my name", ()=>{expect(my.name).toBe("fynn")});test("my age", ()=>{expect(my.age).toBe(27)})})- Describe.only(name, fn)
當(dāng)一個(gè)file有多個(gè)測(cè)試套件,但你只想執(zhí)行其中一個(gè)測(cè)試套件,可以使用 describe.only。
const my = {name : "fynn",age : 27 } let hw = () =>"Hello World!"; describe("my info", ()=>{test("my name", ()=>{expect(my.name).toBe("fynn")});test("my age", ()=>{expect(my.age).toBe(27)})}); describe.only('hw function test suit',()=>{test('hw test',()=>{expect(hw()).toBe("Hello World!");}) })- Describe.skip(name, fn)
一個(gè)file中有多個(gè)測(cè)試套件,如果你想跳過(guò)某個(gè)測(cè)試套件可以使用 describe.skip
- Test
測(cè)試用例,可以寫(xiě)在 describe測(cè)試套件中,也可以單獨(dú)寫(xiě)在測(cè)試套件外面
const my = {name : "fynn",age: 27 } let hw = ()=>"Hello World!" describe("my info",()=>{test("my name",()=>{expect(my.name).toBe("fynn")});test("my age",()=>{expect(my.age).toBe(27)}) }); test("hw test",()=>{expect(hw()).toBe("Hello World!"); })- Test.only
有多個(gè)測(cè)試用例或測(cè)試套件,只想執(zhí)行其中某一個(gè)測(cè)試用例時(shí)可以用test.only。
const my = {name : "fynn",age : 27 }; let hw = ()=>"Hello World!"; test("my name",()=>{expect(my.name).toBe("fynn"); }) test.only("hw test",()=>{expect(hw()).toBe("Hello World!"); })- Test.skip(name, fn)
當(dāng)有多個(gè)測(cè)試用例,想跳過(guò)某個(gè)測(cè)試用例可以使用test.skip;
- It(name,fn)
用法和test一樣,不能嵌套在test中!可以嵌套在describe中
const my = {name : "fynn",age : 27 }; let hw = ()=>"Hello World!"; it("my name",()=>{expect(my.name).toBe("fynn"); }) xit("hw test",()=>{expect(hw()).toBe("Hello World!"); })- AfterAll(fn)
當(dāng)file所有test都執(zhí)行完畢后,執(zhí)行afterAll中的方法。
const my = {name :"fynn",age : 27 }; test("my name",()=>{expect(my.name).toBe("fynn") }); test("my age",()=>{expect(my.age).toBe(27) }); afterAll(()=>{console.log("執(zhí)行完所有test!") })- AfterEach(fn)
每當(dāng)一個(gè)test執(zhí)行完后,調(diào)用一次afterEach中的方法
const my = {name :"fynn",age : 27 }; test("my name",()=>{expect(my.name).toBe("fynn") }); test("my age",()=>{expect(my.age).toBe(27) }); afterEach(()=>{console.log("執(zhí)行完一個(gè)test!") })- BeforeAll(fn)
在所有執(zhí)行test前先調(diào)用beforeAll中的方法
const my = {name :"fynn",age : 27 }; test("my name",()=>{expect(my.name).toBe("fynn") }); test("my age",()=>{expect(my.age).toBe(27) }); beforeAll(()=>{console.log("要開(kāi)始執(zhí)行test了!") });- BeforeEach(fn)
在每個(gè)test執(zhí)行前都會(huì)調(diào)用一次beforeEach中的方法
const my = {name :"fynn",age : 27 }; test("my name",()=>{expect(my.name).toBe("fynn") }); test("my age",()=>{expect(my.age).toBe(27) }); beforeEach(()=>{console.log("要開(kāi)始執(zhí)行一個(gè)test了!") })其它相關(guān)API 參考如下地址:
關(guān)于Jest 官方地址 傳送門(mén)
未完待續(xù)…
總結(jié)
以上是生活随笔為你收集整理的React 之 jest 前端自动化测试的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 三星 SCX-4521NS 网络打印机
- 下一篇: 一个简单HTML5期末考核大作业,学生个