前端單元測(cè)試 工作筆記

單元測(cè)試

  • 定義: 單元測(cè)試是用來對(duì)一個(gè)模塊、一個(gè)函數(shù)或者一個(gè)類來進(jìn)行正確性檢驗(yàn)的測(cè)試工作。

  • 必要性: 在實(shí)際使用過程中會(huì)伴隨著一大批的附帶操作大量增加測(cè)試時(shí)間,并且無法保證其測(cè)試覆蓋率。單元測(cè)試的目的并不僅僅是確認(rèn)是否可用,而是更高效更穩(wěn)定的確認(rèn)其是否可用。一個(gè)函數(shù)在多個(gè)組件中被調(diào)用,但是由于當(dāng)前組件的特殊性需要對(duì)函數(shù)進(jìn)行加工,加工之后對(duì)原來的功能影響是未知的,如果一一將引用函數(shù)的功能全部測(cè)一遍,太浪費(fèi)時(shí)間了,單元測(cè)試解決了重復(fù)測(cè)試相同函數(shù)不同功能的測(cè)試時(shí)間。測(cè)試不通過,根據(jù)需要,要么修改代碼,要么修改測(cè)試

  • 意義: 這種以測(cè)試為驅(qū)動(dòng)的開發(fā)模式(TDD)最大的好處就是確保一個(gè)程序模塊的行為符合我們?cè)O(shè)計(jì)的測(cè)試用例。在將來修改的時(shí)候,可以極大程度地保證該模塊行為仍然是正確的

命名規(guī)則

新建測(cè)試腳本 calcu.test.js,一般命名規(guī)則測(cè)試腳本和原腳本同名,但是后綴名為.test.js (.spec.js)

測(cè)試腳本的樣子

var add = require('./add.js');
var expect = require('chai').expect;

describe('加法函數(shù)的測(cè)試', function() {
  it('1 加 1 應(yīng)該等于 2', function() {
    expect(add(1, 1)).to.be.equal(2);
  });
});
  • 每一段測(cè)試腳本可以獨(dú)立執(zhí)行

  • 測(cè)試腳本里面應(yīng)該包括一個(gè)或多個(gè)describe塊,每個(gè)describe塊應(yīng)該包括一個(gè)或多個(gè)it塊。

  • describe塊稱為"測(cè)試套件"(test suite),表示一組相關(guān)的測(cè)試。它是一個(gè)函數(shù),第一個(gè)參數(shù)是測(cè)試套件的名稱("加法函數(shù)的測(cè)試"),第二個(gè)參數(shù)是一個(gè)實(shí)際執(zhí)行的函數(shù)。

  • it塊稱為"測(cè)試用例"(test case),表示一個(gè)單獨(dú)的測(cè)試,是測(cè)試的最小單位。它也是一個(gè)函數(shù),第一個(gè)參數(shù)是測(cè)試用例的名稱("1 加 1 應(yīng)該等于 2"),第二個(gè)參數(shù)是一個(gè)實(shí)際執(zhí)行的函數(shù)。

  • 所謂"斷言",就是判斷源碼的實(shí)際執(zhí)行結(jié)果與預(yù)期結(jié)果是否一致,如果不一致就拋出一個(gè)錯(cuò)誤。上面這句斷言的意思是,調(diào)用add(1, 1),結(jié)果應(yīng)該等于2

  • 一個(gè)單元測(cè)試?yán)锩婵梢园鄠€(gè)斷言語句

常見的測(cè)試類型

  • 常規(guī)函數(shù)的測(cè)試

  • 異步函數(shù)的測(cè)試

  • api測(cè)試

  1. 常規(guī)

export.add = (a,b) => {
    return a + b;
}

// 測(cè)試腳本如下 calcu.test.js
let calcu = require('./calcu');
let should = require('should');

describe('add func test', () => {
    calcu.add(2,2).should.equal(4);
})

// 運(yùn)行
mocha calcu.test.js
  1. 異步

// 新建book.js
let fs = require('fs);
export.read = (cb) => {
    fs.readFile('./book.txt', 'utf-8', (err, result) => {
        if(err) throw err;
        cb(null, result);
    })
}

// 新建文件book.test.js
let book = require('./book');
let expect = require('chai').expect;

describe('async', () => {
    it('read book async', (done) => {
        book.read((err, result) => {
            expect(err).equal(null);
            expect(result).to.be.a('string');
            done(); // 告訴mocha測(cè)試結(jié)束
        })
    })
})

// 運(yùn)行
mocha book.test.js
// 運(yùn)行mocha book.test.js,我們會(huì)發(fā)現(xiàn)成功了,但是如果我們把book.js增加一個(gè)定時(shí)函數(shù),改為如下例子
let fs = require('fs');

exports.read = (cb) => {
    setTimeout(function() {
        fs.readFile('./book.txt', 'utf-8', (err, result) => {
            if (err) return cb(err);
            console.log("result",result);
            cb(null, result);
        }) 
    }, 3000);
}

// 會(huì)發(fā)現(xiàn)報(bào)如下錯(cuò)誤
// Timeout of 2000ms exceeded.

這是因?yàn)閙ocha默認(rèn)每個(gè)測(cè)試用例最多執(zhí)行2000毫秒,如果到時(shí)沒有得到結(jié)果,就報(bào)錯(cuò)。所以我們?cè)谶M(jìn)行異步操作的時(shí)候,需要額外指定timeout時(shí)間


mocha --timeout 5000 book.test.js

// 指定了超時(shí)時(shí)間是5秒鐘
  1. api測(cè)試
  • api 測(cè)試需要用到一個(gè)模塊是supertest

// 安裝
npm intall supertest --save-dev


// 新建文件 api.test.js
let expect = require('chai').expext;
let request = require("supertest");

describe('api', () => {
    it('get baidu information', function (done) {
        request('https://www.baidu.com')
            .get('/')
            .expect(200)
            .expect('Content-Type', /html/)
            .end(function (err, res){
                expect(res).to.be.an('object');
                done();
            })
    })
})

命令行參數(shù)詳解

  • –reporter :用來指定報(bào)告的格式 默認(rèn)spec 可以另外安裝網(wǎng)頁格式

  • -t 5000是因?yàn)槲覀儨y(cè)試用例中有一個(gè)異步執(zhí)行過程,需要調(diào)高mocha的單元測(cè)試時(shí)間

  • –watch :參數(shù)用來監(jiān)視指定的測(cè)試腳本。只要測(cè)試腳本有變化

  • –bail:參數(shù)指定只要有一個(gè)測(cè)試用例沒有通過,就停止執(zhí)行后面的測(cè)試用例

  • –grep:參數(shù)用來搜索單元測(cè)試用例的名稱,然后運(yùn)行符合搜索條件的測(cè)試用例,支持正則表達(dá)

  • –invert:參數(shù)表示只運(yùn)行不符合條件的測(cè)試腳本,必須與–grep參數(shù)配合使用。

  • --recursive 一般如果運(yùn)行mocha,會(huì)執(zhí)行當(dāng)前目錄下的test目錄的一級(jí)層級(jí)的所有js文件,但是test下的更多層級(jí)卻沒辦法運(yùn)行,這時(shí)就需要參數(shù)–recursive,這時(shí)test子目錄下面所有的測(cè)試用例----不管在哪一層----都會(huì)執(zhí)行

配置文件mocha.opts的配置

  • 每次我們運(yùn)行測(cè)試用例的時(shí)候都需要寫很長(zhǎng)一段命令行,每次都一樣,這樣是不可取的,所以我們可以把這些配置維護(hù)到配置文件里面 (https://cnodejs.org/topic/59e3873520a1a3647d72ac39

mocha的生命鉤子

  • mocha一共四個(gè)生命鉤子

before():在該區(qū)塊的所有測(cè)試用例之前執(zhí)行
after():在該區(qū)塊的所有測(cè)試用例之后執(zhí)行
beforeEach():在每個(gè)單元測(cè)試前執(zhí)行
和afterEach():在每個(gè)單元測(cè)試后執(zhí)行


typescript規(guī)定了數(shù)據(jù)的類型 測(cè)試解決了過程和結(jié)果的正確性


  • 目錄的結(jié)構(gòu)

  • 函數(shù)寫法要求規(guī)范

  • 斷言的框架

  • 測(cè)試框架解決問題?異同

  • dom

單元測(cè)試中應(yīng)該避免

太多的條件邏輯

構(gòu)造函數(shù)中做了太多事情

太多的全局變量

太多的靜態(tài)方法

過多外部依賴

無關(guān)邏輯


測(cè)試代碼時(shí),只考慮測(cè)試,不考慮內(nèi)部實(shí)現(xiàn)

數(shù)據(jù)盡量模擬現(xiàn)實(shí),越靠近現(xiàn)實(shí)越好

充分考慮數(shù)據(jù)的邊界條件

對(duì)重點(diǎn)、復(fù)雜、核心代碼,重點(diǎn)測(cè)試

利用AOP(beforeEach、afterEach),減少測(cè)試代碼數(shù)量,避免無用功能

測(cè)試、功能開發(fā)相結(jié)合,有利于設(shè)計(jì)和代碼重構(gòu)

測(cè)試框架的必要性

就像vue開發(fā)和原生開發(fā)一樣 原生開發(fā)隨著功能的不斷增加,代碼的維護(hù)和功能代碼的添加變得越來越困難

測(cè)試代碼也一樣 單元測(cè)試也需要一種行之有效的實(shí)踐來確保其質(zhì)量和可維護(hù)性。

測(cè)試框架 簡(jiǎn)介 優(yōu)點(diǎn) 不足
QUnit QUnit是jQuery團(tuán)隊(duì)開發(fā)的JavaScript單元測(cè)試工具,功能強(qiáng)大且使用簡(jiǎn)單。目前所有的JQuery代碼都使用QUnit進(jìn)行測(cè)試,原生的JavaScript也可以使用QUnit。 1.使用起來非常方便,有漂亮的外觀和完整的測(cè)試功能(包括異步測(cè)試);?? 2.不需要依賴其它任何軟件包或框架,只要能運(yùn)行JS的地方就可以,QUnit本身只有一個(gè)JS文件和CSS文件,當(dāng)然如果需要可以和jQuery等其它框架集成; ??3.不僅支持在瀏覽器中測(cè)試,還支持在Rhino和node.js等后端測(cè)試。 對(duì)自動(dòng)化支持不好,很難和Ant、Maven或自動(dòng)構(gòu)建等工具集成,主要用在瀏覽器中進(jìn)行測(cè)試。 異步困難語法不流暢不好配置
jasmine Jasmine是一個(gè)有名的JavaScript單元測(cè)試框架,它是獨(dú)立的行為驅(qū)動(dòng)開發(fā)框架,語法清晰易懂。 1.它是基于行為驅(qū)動(dòng)開發(fā)實(shí)現(xiàn)的測(cè)試框架,它的語法非常貼近自然語言簡(jiǎn)單明了容易理解2.它有豐富的API,同時(shí)用戶也支持用戶擴(kuò)展它的API,這一點(diǎn)很少有其它框架能夠做到。 在瀏覽器中的測(cè)試界面不如QUnit美觀、詳細(xì)。一異步測(cè)試麻煩
Mocha Mocha是一個(gè)簡(jiǎn)單、靈活有趣的JavaScript 測(cè)試框架,用于Node.js和瀏覽器上的JavaScript應(yīng)用測(cè)試 1.支持簡(jiǎn)單異步,包括 promise;2.提供javascript API來運(yùn)行測(cè)試;3.non-ttys自動(dòng)檢測(cè)和禁用顏色;4.支持異步測(cè)試超時(shí);5.支持node debugger;6.高擴(kuò)展性 部分領(lǐng)域缺少支持

斷言庫

  • chai是一套TDD(測(cè)試驅(qū)動(dòng)開發(fā))/BDD(行為驅(qū)動(dòng)開發(fā))的斷言框架他包含有3個(gè)斷言庫,支持BDD風(fēng)格的expect/should和TDD風(fēng)格的assert,這里主要說明expect/should庫,BDD風(fēng)格說簡(jiǎn)單的就是你的測(cè)試代碼更加的語義化,讓你的斷言可讀性更好,expect/should庫都支持鏈?zhǔn)秸{(diào)用

(測(cè)試驅(qū)動(dòng)開發(fā)): 它要求在編寫某個(gè)功能的代碼之前先編寫測(cè)試代碼,然后只編寫使測(cè)試通過的功能代碼,通過測(cè)試來推動(dòng)整個(gè)開發(fā)的進(jìn)行。這有助于編寫簡(jiǎn)潔可用和高質(zhì)量的代碼,并加速開發(fā)過程。測(cè)試驅(qū)動(dòng)開發(fā)的基本過程如下:

1) 明確當(dāng)前要完成的功能。可以記錄成一個(gè) TODO 列表。

2) 快速完成針對(duì)此功能的測(cè)試用例編寫。

3) 測(cè)試代碼編譯不通過。

4) 編寫對(duì)應(yīng)的功能代碼。

5) 測(cè)試通過。

6) 對(duì)代碼進(jìn)行重構(gòu),并保證測(cè)試通過。

7) 循環(huán)完成所有功能的開發(fā)。

(行為驅(qū)動(dòng)開發(fā)): 使用通用語言,客戶和開發(fā)者可以一起定義出系統(tǒng)的行為,從而做出符合客戶需求的設(shè)計(jì)。但如果光有設(shè)計(jì),而沒有驗(yàn)證的手段,就無法檢驗(yàn)我們的實(shí)現(xiàn)是不是符合設(shè)計(jì)。所以 BDD還是要和測(cè)試結(jié)合在一起,用系統(tǒng)行為的定義來驗(yàn)證實(shí)現(xiàn)代碼。(有點(diǎn)厲害)

Mocha + Chai

import chai from 'chai';

const assert = chai.assert;
const expect = chai.expect;  // 這個(gè)比較貼合自然語言
const should = chai.should();
// -----------
foo.should.be.a('string');
foo.should.equal('bar');
list.should.have.length(3);
obj.should.have.property('name');

expect(foo).to.be.a('string');
expect(foo).to.equal('bar');
expect(list).to.have.length(3);
expect(obj).to.have.property('flavors');

assert.typeOf(foo, 'string');
assert.equal(foo, 'bar');
assert.lengthOf(list, 3);
assert.property(obj, 'flavors');

測(cè)試思路

基本思路:自身從函數(shù)的調(diào)用者出發(fā),對(duì)函數(shù)進(jìn)行各種情況的調(diào)用,查看其容錯(cuò)程度、返回結(jié)果是否符合預(yù)期。

目錄

參考別人

參考vue-cli
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,363評(píng)論 6 532
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,497評(píng)論 3 416
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,305評(píng)論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,962評(píng)論 1 311
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,727評(píng)論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,193評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,257評(píng)論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,411評(píng)論 0 288
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,945評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,777評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,978評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,519評(píng)論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,216評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,642評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,878評(píng)論 1 286
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,657評(píng)論 3 391
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,960評(píng)論 2 373

推薦閱讀更多精彩內(nèi)容