前端自動(dòng)化測(cè)試--02基礎(chǔ)

目前開(kāi)發(fā)大型應(yīng)用缀辩,測(cè)試是一個(gè)非常重要的環(huán)節(jié)臭埋,但是大多數(shù)前端開(kāi)發(fā)者對(duì)測(cè)試相關(guān)的知識(shí)是比較缺乏的。因?yàn)榭赡茼?xiàng)目開(kāi)發(fā)周期短根本沒(méi)有機(jī)會(huì)寫臀玄,所以你沒(méi)有辦法體會(huì)到前端自動(dòng)化測(cè)試的重要性瓢阴。

來(lái)說(shuō)說(shuō)為什么前端自動(dòng)化測(cè)試如此重要!

先看看前端常見(jiàn)的問(wèn)題:

  • 修改某個(gè)模塊功能時(shí)健无,其它模塊也受影響荣恐,很難快速定位bug
  • 多人開(kāi)發(fā)代碼越來(lái)越難以維護(hù)
  • 不方便迭代,代碼無(wú)法重構(gòu)
  • 代碼質(zhì)量差

增加自動(dòng)化測(cè)試后:

  • 我們?yōu)楹诵墓δ芫帉憸y(cè)試后可以保障項(xiàng)目的可靠性
  • 強(qiáng)迫開(kāi)發(fā)者編寫更容易被測(cè)試的代碼,提高代碼質(zhì)量
  • 編寫的測(cè)試有文檔的作用叠穆,方便維護(hù)

1.測(cè)試簡(jiǎn)介

1.1 黑盒測(cè)試和白盒測(cè)試

  • 黑盒測(cè)試一般也被稱為功能測(cè)試少漆,黑盒測(cè)試要求測(cè)試人員將程序看作一個(gè)整體,不考慮其內(nèi)部結(jié)構(gòu)和特性硼被,只是按照期望驗(yàn)證程序是否能正常工作
  • 白盒測(cè)試是基于代碼本身的測(cè)試示损,一般指對(duì)代碼邏輯結(jié)構(gòu)的測(cè)試。

1.2 測(cè)試分類

單元測(cè)試(Unit Testing)

單元測(cè)試是指對(duì)程序中最小可測(cè)試單元進(jìn)行的測(cè)試嚷硫,例如測(cè)試一個(gè)函數(shù)检访、一個(gè)模塊一個(gè)組件...

集成測(cè)試(Integration Testing)

將已測(cè)試過(guò)的單元測(cè)試函數(shù)進(jìn)行組合集成暴露出的高層函數(shù)或類的封裝论巍,對(duì)這些函數(shù)或類進(jìn)行的測(cè)試

端到端測(cè)試(E2E Testing)
打開(kāi)應(yīng)用程序模擬輸入烛谊,檢查功能以及界面是否正確

1.3 TDD & BDD

TDD是測(cè)試驅(qū)動(dòng)開(kāi)發(fā)(Test-Driven Development)

TDD的原理是在開(kāi)發(fā)功能代碼之前,先編寫單元測(cè)試用例代碼

BDD是行為驅(qū)動(dòng)開(kāi)發(fā)(Behavior-Driven Development)

系統(tǒng)業(yè)務(wù)專家嘉汰、開(kāi)發(fā)者丹禀、測(cè)試人員一起合作,分析軟件的需求鞋怀,然后將這些需求寫成一個(gè)個(gè)的故事双泪。開(kāi)發(fā)者負(fù)責(zé)填充這些故事的內(nèi)容,保證程序?qū)崿F(xiàn)效果與用戶需求一致密似。

小結(jié):

TDD是先寫測(cè)試再開(kāi)發(fā) (一般都是單元測(cè)試焙矛,白盒測(cè)試);而B(niǎo)DD則是按照用戶的行為來(lái)開(kāi)發(fā)残腌,再根據(jù)用戶的行為編寫測(cè)試用例 (一般都是集成測(cè)試村斟,黑盒測(cè)試)

1.4 測(cè)試框架

  • Karma:Karma為前端自動(dòng)化測(cè)試提供了跨瀏覽器測(cè)試的能力,可以在瀏覽器中執(zhí)行測(cè)試用例
  • Mocha:前端自動(dòng)化測(cè)試框架抛猫,需要配合其他庫(kù)一起使用蟆盹,像chai、sinon...
  • Jest:Jest 是Facebook推出的一款測(cè)試框架闺金,集成了 Mocha,chai,jsdom,sinon等功能逾滥。
  • ...

看到這里Facebook 都在推Jest,你還不學(xué)嗎? Jest也有一些缺陷就是不能像Karma這樣直接跑在瀏覽器上败匹,它采用的是jsdom寨昙,優(yōu)勢(shì)是簡(jiǎn)單、0配置掀亩! 后續(xù)我們通過(guò)Jest來(lái)聊聊前端自動(dòng)化測(cè)試舔哪。

2.Jest的核心應(yīng)用

在說(shuō)Jest測(cè)試之前,先來(lái)看看以前我們是怎樣測(cè)試的

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=webyouxuan')); // {name:'webyouxuan'}
// console.log(stringify({name:'webyouxuan'})) // name=webyouxuan

我們每寫完一個(gè)功能归榕,都先需要手動(dòng)測(cè)試功能是否正常尸红,測(cè)試后可能會(huì)將測(cè)試代碼注釋起來(lái),這樣會(huì)產(chǎn)生一系列問(wèn)題。因?yàn)闀?huì)污染源代碼外里,所有的測(cè)試代碼和源代碼混合在一起怎爵。如果刪除掉,下次測(cè)試還需要重新編寫盅蝗。

所以測(cè)試框架就幫我們解決了上述的問(wèn)題

2.1 分組鳖链、用例

Jest是基于模塊的,我們需要將代碼包裝成模塊的方式墩莫,分別使用 exportparser芙委、stringify這兩個(gè)方法導(dǎo)出。

安裝jest

npm init -y # 初始化pacakge.json
npm i jest  

我們建立一個(gè)qs.test.js來(lái)專門編寫測(cè)試用例狂秦,這里的用例你可以認(rèn)為就是一條測(cè)試功能 (后綴要以.test.js結(jié)尾灌侣,這樣jest測(cè)試時(shí)默認(rèn)會(huì)調(diào)用這個(gè)文件)。

import {parser,stringify} from './qs';

it('測(cè)試 parser 是否能正常解析結(jié)果',()=>{
    // expect 斷言裂问,判斷解析出來(lái)的結(jié)果是否和 {name:'webyouxuan'}相等
    expect(parser(`name=webyouxuan`)).toEqual({name:'webyouxuan'});
})

jest默認(rèn)自帶斷言功能侧啼,斷言的意思就是判斷是不是這個(gè)樣子,我斷定你今天沒(méi)吃飯~堪簿,結(jié)果你吃了痊乾,說(shuō)明這次斷言就失敗了,測(cè)試就無(wú)法通過(guò)椭更。

通過(guò)配置scripts 來(lái)執(zhí)行命令

"scripts": {
    "test": "jest"
}

執(zhí)行 npm run test哪审,可惜的是默認(rèn)在node環(huán)境下不支持es6模塊的語(yǔ)法,需要babel轉(zhuǎn)義虑瀑,當(dāng)然你也可以直接使用commonjs規(guī)范來(lái)導(dǎo)出方法湿滓,因?yàn)榇蠖鄶?shù)現(xiàn)在開(kāi)發(fā)都采用es6模塊,所以安裝一下即可舌狗。

# core是babel的核心包 preset-env將es6轉(zhuǎn)化成es5
npm i @babel/core @babel/preset-env --save-dev

并且需要配置.babelrc文件茉稠,告訴babel用什么來(lái)轉(zhuǎn)義

{
    "presets":[
        [
            "@babel/preset-env",{
                "targets": {"node":"current"}
            }
        ]
    ]
}

默認(rèn)Jest中集成了babel-jest,運(yùn)行時(shí)默認(rèn)會(huì)調(diào)用.babelrc進(jìn)行轉(zhuǎn)義把夸,可以直接將es6轉(zhuǎn)成es5語(yǔ)法。
運(yùn)行 npm run test 出現(xiàn):

繼續(xù)編寫第二個(gè)用例

import {parser,stringify} from './qs';
describe('測(cè)試qs 庫(kù)',()=>{
    it('測(cè)試 parser 是否能正常解析結(jié)果',()=>{
        expect(parser(`name=webyouxuan`)).toEqual({name:'webyouxuan'});
    });

    it('測(cè)試 stringify 是否正常使用stringify',()=>{
        expect(stringify({name:'webyouxuan'})).toEqual(`name=webyouxuan`)
    })
});

describe的功能是給用例分組铭污,這樣可以更好的給用例分類恋日,其實(shí)這就是我們所謂的單元測(cè)試,對(duì)某個(gè)具體函數(shù)和功能進(jìn)行測(cè)試嘹狞。

2.2 matchers匹配器

在寫第一個(gè)測(cè)試用例時(shí)岂膳,我們一直在使用toEqual其實(shí)這就是一個(gè)匹配器,那我們來(lái)看看jest中常用的匹配器有哪些磅网?因?yàn)槠ヅ淦魈嗔颂附兀晕揖椭v些常用的!

為了方便理解,我把匹配器分為三類:判斷相等簸喂、不等毙死、是否包含。

it('判斷是否相等',()=>{
    expect(1+1).toBe(2); // 相當(dāng)于 js中的===
    expect({name:'webyouxuan'}).toEqual({name:'webyouxuan'}); // 比較內(nèi)容是否相等
    expect(true).toBeTruthy(); // 是否為 true / false 也可以用toBe(true)
    expect(false).toBeFalsy();
});

it('判斷不相等關(guān)系',()=>{
    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 測(cè)試操作節(jié)點(diǎn)方法

說(shuō)了半天喻鳄,我們自己來(lái)寫個(gè)功能測(cè)試一下!

export const removeNode = (node) => {
    node.parentNode.removeChild(node)
};

核心就是測(cè)試傳入一個(gè)節(jié)點(diǎn)扼倘,這個(gè)節(jié)點(diǎn)是否能從DOM中刪除

import { removeNode } from './dom'
it('測(cè)試刪除節(jié)點(diǎn)',()=>{
    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()
})

這個(gè)就是我們所說(shuō)的jsdom,在node中操作DOM元素

2.4 Jest常用命令

我們希望每次更改測(cè)試后除呵,自動(dòng)重新執(zhí)行測(cè)試再菊,修改執(zhí)行命令:

"scripts": {
    "test": "jest --watchAll"
}

重新執(zhí)行 npm run test,這時(shí)就會(huì)監(jiān)控用戶的修改

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末颜曾,一起剝皮案震驚了整個(gè)濱河市纠拔,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌泛豪,老刑警劉巖稠诲,帶你破解...
    沈念sama閱讀 218,546評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異候址,居然都是意外死亡吕粹,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,224評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門岗仑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)匹耕,“玉大人,你說(shuō)我怎么就攤上這事荠雕∥绕洌” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 164,911評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵炸卑,是天一觀的道長(zhǎng)既鞠。 經(jīng)常有香客問(wèn)我,道長(zhǎng)盖文,這世上最難降的妖魔是什么嘱蛋? 我笑而不...
    開(kāi)封第一講書人閱讀 58,737評(píng)論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮五续,結(jié)果婚禮上洒敏,老公的妹妹穿的比我還像新娘。我一直安慰自己疙驾,他們只是感情好凶伙,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,753評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著它碎,像睡著了一般函荣。 火紅的嫁衣襯著肌膚如雪显押。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 51,598評(píng)論 1 305
  • 那天傻挂,我揣著相機(jī)與錄音乘碑,去河邊找鬼。 笑死踊谋,一個(gè)胖子當(dāng)著我的面吹牛蝉仇,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播殖蚕,決...
    沈念sama閱讀 40,338評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼轿衔,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了睦疫?” 一聲冷哼從身側(cè)響起害驹,我...
    開(kāi)封第一講書人閱讀 39,249評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蛤育,沒(méi)想到半個(gè)月后宛官,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,696評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡瓦糕,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,888評(píng)論 3 336
  • 正文 我和宋清朗相戀三年底洗,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片咕娄。...
    茶點(diǎn)故事閱讀 40,013評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡亥揖,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出圣勒,到底是詐尸還是另有隱情费变,我是刑警寧澤,帶...
    沈念sama閱讀 35,731評(píng)論 5 346
  • 正文 年R本政府宣布圣贸,位于F島的核電站挚歧,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏吁峻。R本人自食惡果不足惜滑负,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,348評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望用含。 院中可真熱鬧橙困,春花似錦、人聲如沸耕餐。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,929評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)肠缔。三九已至夏跷,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間明未,已是汗流浹背槽华。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,048評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留趟妥,地道東北人猫态。 一個(gè)月前我還...
    沈念sama閱讀 48,203評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像披摄,于是被迫代替她去往敵國(guó)和親亲雪。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,960評(píng)論 2 355