亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

前端單元測(cè)試之UI測(cè)試功能性代碼測(cè)試教程

 更新時(shí)間:2022年08月05日 11:01:42   作者:VERMONT23  
這篇文章主要為大家介紹了前端單元測(cè)試之UI測(cè)試及功能性代碼測(cè)試教程示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

前言

《孤勇者》最近火爆的一塌糊涂,占領(lǐng)了小學(xué)生、甚至幼兒園,連我家2歲多的兒子盡然也會(huì)哼幾句。雖然他以為這首歌是奧特曼的主題曲。

回到正題,現(xiàn)代前端項(xiàng)目的工程化、模板化日益壯大,各種類庫(kù)框架層出不窮,整個(gè)行業(yè)儼然一副百花齊放百家爭(zhēng)鳴的狀態(tài)。而對(duì)于前端單元測(cè)試而言,在整個(gè)前端領(lǐng)域,實(shí)際上單元測(cè)試早已不算什么冷門(mén)話題,各種測(cè)試框架、庫(kù)已經(jīng)非常完善。

對(duì)于一個(gè)前端開(kāi)發(fā)而言,實(shí)際上我們不太需要專注所有的這些測(cè)試框架、工具。我們只需要按照自己所需(團(tuán)隊(duì)、項(xiàng)目所需)選擇適合自己的即可。

而怎么選型,實(shí)際也非常簡(jiǎn)單。在現(xiàn)在前端項(xiàng)目多數(shù)基于框架的大前提下,直接從官方文檔上找單元測(cè)試的部分。官方必然會(huì)提供測(cè)試庫(kù)或者推薦優(yōu)秀的社區(qū)測(cè)試庫(kù)。

從前端項(xiàng)目層面,無(wú)非就是UI的測(cè)試(簡(jiǎn)單的理解成DOM的測(cè)試) 、功能性代碼測(cè)試。

UI測(cè)試:

很容易理解,前端的項(xiàng)目終究還是會(huì)展示在瀏覽器上,那么所謂的UI測(cè)試也就是測(cè)試我們寫(xiě)的代碼和預(yù)期渲染的UI是否一致而已。

結(jié)合到我們現(xiàn)在實(shí)際項(xiàng)目,多數(shù)基于UI框架的情況(比如React),我們只需要跟隨官方提供的UI測(cè)試途徑即可,千萬(wàn)別去直接搜索XX前端單元測(cè)試框架之類的。每個(gè)框架都有自己的特性,或多或少都會(huì)涉及到UI的渲染,比如虛擬DOM、生命周期之類。所以像React官方文檔提到的@testing-library/react(也有曾經(jīng)使用過(guò)比較優(yōu)秀的Enzyme),都會(huì)根據(jù)React框架特性提供相關(guān)的API例如:render、renderHook之類的。

本文對(duì)于UI的測(cè)試不進(jìn)行詳細(xì)介紹。

功能性代碼測(cè)試:

簡(jiǎn)單的說(shuō)就是一段封裝的代碼,無(wú)論是一個(gè)函數(shù)或者是一個(gè)類。而這段代碼多數(shù)情況和UI無(wú)關(guān),我們只需要要關(guān)注功能實(shí)現(xiàn)的本身,最簡(jiǎn)單的就是一個(gè)函數(shù),輸入什么參數(shù),返回什么值。

功能性代碼的測(cè)試將是本文講述的重點(diǎn),當(dāng)然我們需要一個(gè)測(cè)試框架提供支持,本文講述的Jest就是這種一個(gè)框架。

在介紹Jest之前,還是先聊一聊何謂單元測(cè)試,進(jìn)行有效的單元測(cè)試。

讓人聞風(fēng)喪膽的單元測(cè)試

說(shuō)起單元測(cè)試,尤其是對(duì)于前端行業(yè)的同學(xué)們來(lái)說(shuō),真的算的上是讓人聞風(fēng)喪膽,畢竟單測(cè)這個(gè)詞聽(tīng)上去就是牛逼轟轟。

也經(jīng)常有勇士會(huì)說(shuō),勞資React、antd一把梭哈,要個(gè)雞兒的單元測(cè)試。

現(xiàn)實(shí)工作中,當(dāng)你接收了別人的代碼,大多數(shù)人可能會(huì)覺(jué)得在接手一座屎山,得想辦法怎么在屎山上進(jìn)行雕花,而且還得讓山不崩塌。阿西吧,勞資還不如重構(gòu)了。

stop!又雙叒叕扯遠(yuǎn)了。

好了,別管測(cè)試同學(xué)嘴里的什么黑盒、白盒、性能、功能。對(duì)我們大多數(shù)人而言,單元測(cè)試,不就是對(duì)代碼進(jìn)行測(cè)試么。怎么測(cè)試呢?

代碼測(cè)試代碼

可能你有點(diǎn)明白了,那么問(wèn)題來(lái)了,挖掘機(jī)哪家強(qiáng)?sorry,問(wèn)題是如何用代碼測(cè)試代碼?

舉個(gè)簡(jiǎn)單的例子,你寫(xiě)個(gè)了函數(shù),判斷請(qǐng)求是否成功。因?yàn)椴豢煽咕艿囊蛩兀銈児緢F(tuán)隊(duì)里面的后端同學(xué),每個(gè)人的接口對(duì)于成功返回的定義不一樣。有的是通過(guò)http 狀態(tài)碼200,有的根據(jù)返回值中的success是否為true,有的人返回了一個(gè)code字段,值可能為200也可能為"200"。

現(xiàn)實(shí)就是這么殘酷,前端的小伙伴總是在寫(xiě)這樣那樣的兼容方案。

// utils.js
/**
 * 判斷請(qǐng)求是否成功,成功返回,失敗提示失敗信息
 * @param {Object} response
 * @returns
 */
export const requestSuccess = response => {
    if (
        response.data.success === true ||
        response.data.code === 'success' ||
        response.data.code === '1000' ||
        response.data.code === 200 ||
        response.data.code === '200'
    ) {
        return response;
    }
    message.error(response.data.message || '請(qǐng)求失敗');
};

對(duì)于這樣一個(gè)常見(jiàn)的函數(shù)而言,我們需要如何對(duì)其進(jìn)行單元測(cè)試呢?

所謂測(cè)試,無(wú)非就是在特定場(chǎng)景下(我們可以先不用管這個(gè)場(chǎng)景是什么),輸入一些值,得到輸出值,然后跟期望輸出的值進(jìn)行比對(duì),看是否一致,如果一致則表明這一條測(cè)試用例通過(guò)。

看這個(gè)簡(jiǎn)單的例子,我們不用管下面的testexpect方法具體是干什么的。

// utils.test.js
import { requestSuccess } from './utils.js'
test('requestSuccess方法 請(qǐng)求正常測(cè)試用例', () => {
    const input = {
        data: {
            success: true,
            code: 200,
            message: '請(qǐng)求成功'
        }
    };
    const expectOutput = input;
    const output = requestSuccess(input);
    expect(output).toEqual(expectOutput);
});

在上面的案例中,我們來(lái)逐步分解:

首先定義了一個(gè)輸入:input。

然后將input作為參數(shù),調(diào)用了requestSuccess方法,本次調(diào)用得到另一個(gè)返回值output。

最后就是判定,判定(也就是所謂的期望/斷言)得到的輸出值output等于期望的輸出值expectOutput。

這是一段最基礎(chǔ)的,正常輸入值的單元測(cè)試代碼,我們可以總結(jié)出大概的步驟:

  • 1、定義輸入值
  • 2、將輸入值帶上,執(zhí)行代碼,得到輸出值
  • 3、對(duì)輸出值進(jìn)行斷言

這個(gè)斷言就是說(shuō)你覺(jué)得這個(gè)輸出值應(yīng)該是什么,也斷言這個(gè)輸出值和你期望輸出值匹配。當(dāng)然,實(shí)際輸出值和你的期望輸出可能不匹配,那就是表明你的這條用例執(zhí)行失敗。失敗的原因可能是你的源代碼有問(wèn)題,也可能是你單元測(cè)試的用例代碼有問(wèn)題。

OK,我們了解了最基礎(chǔ)的單元測(cè)試。那么真正意義的單元測(cè)試應(yīng)該怎么寫(xiě)呢?

無(wú)非就是寫(xiě)單元測(cè)試用例,定義各種輸入值,判斷和期望輸出是否一致,然后進(jìn)行分析修改。

再回歸到上面的requestSuccess方法,上面的測(cè)試用例僅僅是驗(yàn)證了正常情況下,當(dāng)然這種情況可能占大多數(shù),但是單元測(cè)試一般就是為了兼容小部分的異常場(chǎng)景。

那么接下來(lái),我們就來(lái)分析下一般意義上請(qǐng)求失敗場(chǎng)景的測(cè)試用例:

// utils.test.js
import { requestSuccess } from './utils';
test('requestSuccess方法 請(qǐng)求失敗測(cè)試用例', () => {
    const input = {
        data: {
            success: false,
            message: '請(qǐng)求失敗'
        }
    };
    const output = requestSuccess(input); // 沒(méi)有返回值,output為undefine
    expect(output).toBeUndefined(); 
});

好了,到這里,有的同學(xué)說(shuō),請(qǐng)求正常、請(qǐng)求異常的情況都覆蓋了,單元測(cè)試完成,可以提交測(cè)試,然后進(jìn)行愉快的摸魚(yú)了。

等等,事情沒(méi)有那么簡(jiǎn)單。

測(cè)試同學(xué)急急忙忙來(lái)找你了,說(shuō)你的程序又崩了,頁(yè)面空白了。

你讓測(cè)試同學(xué)給你復(fù)現(xiàn)了,一步一步debug。原來(lái)發(fā)現(xiàn),調(diào)用你requestSuccess方法的response參數(shù),盡然為一個(gè)空對(duì)象: {} 。

你可能會(huì)直呼好家伙,后端不講武德?。ó?dāng)然可能性很多,可能并不是后端一個(gè)人的鍋),因?yàn)椴豢煽咕芤蛩?,你又得去改代碼,一邊改一邊罵。

改完之后的源碼如下,然后你又得意的告訴測(cè)試同學(xué)已經(jīng)改完,沒(méi)有問(wèn)題了。

export const requestSuccess = response => {
    if (
        response.data?.success === true ||
        response.data?.code === 'success' ||
        response.data?.code === '1000' ||
        response.data?.code === 200 ||
        response.data?.code === '200'
    ) {
        return response;
    }
    message.error(response.data.message || '請(qǐng)求失敗');
};

結(jié)果不一會(huì),測(cè)試同學(xué)說(shuō),你的程序又崩了,頁(yè)面空白了。

你慌了,自言自語(yǔ)的說(shuō)道,沒(méi)問(wèn)題啊,勞資都寫(xiě)了兼容了,讓測(cè)試同學(xué)給你復(fù)現(xiàn)了,一步一步debug。原來(lái)發(fā)現(xiàn),調(diào)用你requestSuccess方法的response參數(shù),盡然為undefined。你破口大罵,告訴測(cè)試是后端的鍋,是另一個(gè)前端瞎雞兒調(diào)用,和你無(wú)關(guān)。掰扯了一段時(shí)間,你又改了下你的代碼:

// 當(dāng)然下面的代碼還是可以繼續(xù)優(yōu)化
export const requestSuccess = response => {
    if (
        response?.data?.success === true ||
        response?.data?.code === 'success' ||
        response?.data?.code === '1000' ||
        response?.data?.code === 200 ||
        response?.data?.code === '200'
    ) {
        return response;
    }
    message.error(response.data.message || '請(qǐng)求失敗');
};

再回到單元測(cè)試的正題上,上面的那些異常情況,在實(shí)際項(xiàng)目運(yùn)行中比比皆是。而除了配合測(cè)試同學(xué)發(fā)現(xiàn)bug然后修改之外,我們?cè)趩卧獪y(cè)試的時(shí)候即可發(fā)現(xiàn),并優(yōu)化自己的代碼。

例如requestSuccess針對(duì)這個(gè)方法而言,我們先不用去管實(shí)際調(diào)用時(shí)候什么請(qǐng)求成功、請(qǐng)求失敗,只去針對(duì)這個(gè)方法本身,調(diào)用requestSuccess方法的參數(shù)可能性是非常多的,各種類型的,所以我們可以以每一種類型的輸入值作為一條測(cè)試用例。

// utils.test.js
import { requestSuccess } from './utils';
// 這個(gè)describe可以不用糾結(jié),理解成幾份用例的集合,只是統(tǒng)一為異常輸入的描述
describe('requestSuccess方法異常輸入測(cè)試用例', () => {
    test('response為空對(duì)象測(cè)試', () => {
        const input = {};
        const output = requestSuccess(input);
        expect(output).toBeUndefined();
    });
    test('response為undefined測(cè)試', () => {
        const output = requestSuccess();
        expect(output).toBeUndefined();
    });
    test('response為Number類型測(cè)試', () => {
        const output = requestSuccess(123);
        expect(output).toBeUndefined();
    });
});

在寫(xiě)了這么多的異常輸入的測(cè)試用例之后,你會(huì)發(fā)現(xiàn)你一開(kāi)始寫(xiě)的requestSuccess不夠強(qiáng)大,導(dǎo)致單元測(cè)試用例執(zhí)行失敗,所以你可以一遍又一遍的修改你的源碼,直至測(cè)試用例都通過(guò)。

總結(jié): 如何進(jìn)行有效的單元測(cè)試,最簡(jiǎn)單的做法就是考慮各種異常/邊界輸入值,編寫(xiě)相應(yīng)的測(cè)試用例,通過(guò)單元測(cè)試的執(zhí)行,優(yōu)化你的代碼。

當(dāng)然做好單元測(cè)試,并不僅僅只是說(shuō)考慮各種異常輸入即可,實(shí)際還會(huì)涉及到開(kāi)發(fā)時(shí)候 的考慮(比如常說(shuō)的測(cè)試驅(qū)動(dòng)開(kāi)發(fā)之類的)以及非常多的實(shí)現(xiàn)細(xì)節(jié),這個(gè)可能就需要你慢慢的理解了。

Jest介紹

官方鏈接

Jest is a delightful JavaScript Testing Framework with a focus on simplicity.

It works with projects using: Babel, TypeScript, Node, React, Angular, Vue and more!

官方的介紹就是上面2段話,就是說(shuō)jest是一個(gè)讓人愉悅的js測(cè)試框架,專注于簡(jiǎn)單性??梢耘浜蟗abel、ts、node、react、angular、vue等其他庫(kù) 一起使用。

我們前文提及的什么describe、test、expect方法等等在Jest中都有相應(yīng)的api。

一、基礎(chǔ)教程

安裝

可以使用yarn或者npm進(jìn)行安裝

yarn add jest -D | npm i jest -D

源碼開(kāi)發(fā)

這里舉了一個(gè)簡(jiǎn)單的例子,實(shí)際組件開(kāi)發(fā)需要使用ts以及其他UI測(cè)試框架。

例如開(kāi)發(fā)一個(gè)基礎(chǔ)方法,返回2個(gè)參數(shù)的和。文件名為sum.ts

// sum.js
function sum(a, b) {
  return a + b;
}
export default sum;

測(cè)試用例編寫(xiě)

首先我們根據(jù)上面的目標(biāo)文件(sum.js)創(chuàng)建一個(gè)測(cè)試用例文件-- sum.test.js, 測(cè)試用例文件名稱統(tǒng)一為*.test.js(后綴根據(jù)實(shí)際場(chǎng)景區(qū)分為.js或者.ts或者.tsx)

// sum.test.js
import sum from './sum';
test('adds 1 + 2 to equal 3', () => {
  expect(sum(1, 2)).toBe(3);
});

開(kāi)始測(cè)試

添加下面的部分到你的package.json中

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

最后,執(zhí)行yarn testornpm run test命令,Jest將會(huì)打印如下信息:

PASS  ./sum.test.js
? adds 1 + 2 to equal 3 (5ms)

就這樣,基于jest的一個(gè)基礎(chǔ)單元測(cè)試流程走好了,Jest的單元測(cè)試核心就是在test方法的第二個(gè)參數(shù)里面,expect方法返回一個(gè)期望對(duì)象,通過(guò)匹配器(例如toBe)進(jìn)行斷言,期望是否和你預(yù)期的一致,和預(yù)期一致則單元測(cè)試通過(guò),不一致則測(cè)試無(wú)法通過(guò),需要排除問(wèn)題然后繼續(xù)進(jìn)行單元測(cè)試。

更多的配置以及命令行參數(shù)請(qǐng)參考官方文檔下面開(kāi)始講解一些核心API。

二、核心API

全局方法

在你的測(cè)試文件中,Jest將下面這些方法和對(duì)象放置全局環(huán)境,所以你無(wú)需再顯式的去require或者import。當(dāng)然,如果你更喜歡顯式的import,也可以使用例如import { describe, expect, it } from '@jest/globals'的方式。

test(name, fn, timeout)

test有別名it,兩個(gè)方法是一樣的。

第一個(gè)參數(shù)是你想要描述的測(cè)試用例名稱; 第二個(gè)參數(shù)是包含測(cè)試期望的函數(shù),也是測(cè)試用例的核心。第三個(gè)參數(shù)(可選)是超時(shí)時(shí)間,也就是超過(guò)多久將會(huì)取消測(cè)試(默認(rèn)是5秒鐘)

Note: 如果fn返回的是個(gè)promise,Jest在完成測(cè)試前將會(huì)等待Promise達(dá)到resolved狀態(tài)。具體情況本文下面也會(huì)講到如何對(duì)異步代碼進(jìn)行測(cè)試。

匹配器

Jest使用匹配器可以讓你使用各種方式測(cè)試你的代碼,Jest中的匹配器實(shí)際就是expect方法返回的期望對(duì)象中包含的相關(guān)方法。官方提供了非常多的匹配器,完善的學(xué)習(xí)請(qǐng)查看官方文檔

下面摘選了幾個(gè)最常見(jiàn)的匹配器方法。

1、.toBe(value)

toBe是最簡(jiǎn)單最基礎(chǔ)的匹配器,就是判定是否精確匹配,toBe方法使用了Object.is方法來(lái)測(cè)試精確相等。

Object.is方法的判定準(zhǔn)則可以參考這里

test('two plus two is four', () => {
  expect(2 + 2).toBe(4);
});

在測(cè)試中,你有時(shí)需要區(qū)分 undefined、 null,和 false,但有時(shí)你又不需要區(qū)分。 Jest 讓你明確你想要什么。

toBeNull 只匹配 null

toBeUndefined 只匹配 undefined

toBeDefinedtoBeUndefined 相反

toBeTruthy 匹配任何 if 語(yǔ)句為真

toBeFalsy 匹配任何 if 語(yǔ)句為假

2、.not

非常容易理解,一般就是反向測(cè)試

test('two plus two is four', () => {
  expect(2 + 2).not.toBe(4);
});

3、.toEqual

遞歸檢查對(duì)象或數(shù)組的每個(gè)字段

和上面的toBe進(jìn)行對(duì)比,toBe對(duì)比倆對(duì)象對(duì)比的是內(nèi)存地址,toEqual比的是屬性值。

test('object assignment', () => {
  const data1 = { one: 1, two: 2 };
  const data2 = { one: 1, two: 2 };
  expect(data1).toBe(data2); // 測(cè)試失敗
  expect(data1).toEqual(data2);// 測(cè)試通過(guò)
});

4、expect.assertions

expect.assertions(number) 驗(yàn)證一定數(shù)量的斷言在某個(gè)測(cè)試用例中被調(diào)用。通常在異步代碼測(cè)試中非常有用,目的就是為了確保所有的斷言被真實(shí)的調(diào)用了。

比如下面這個(gè)例子,如果去掉了expect.assertions(2), 那么測(cè)試用例會(huì)通過(guò)測(cè)試,但實(shí)際的需求應(yīng)該是失敗的,因?yàn)槲覀冏畛醯钠谕莄atch中的斷言也會(huì)被調(diào)用。

而有了expect.assertions(2),Jest會(huì)判斷斷言實(shí)際調(diào)用的數(shù)量和我們預(yù)期是否一致,如果不一致則說(shuō)明測(cè)試失敗。

test('doAsync calls both callbacks', () => {
  expect.assertions(2);
  return Promise.resolve(123).then((data: number) => {
    expect(data).toBe(123);
    return; // 例如手抖寫(xiě)了return
    // 因?yàn)槟承┰蛳旅娴拇a沒(méi)有執(zhí)行
    throw new Error('報(bào)錯(cuò)了');
  }).catch(err => {
    expect(err).toBe('報(bào)錯(cuò)了');
  });
});

異步代碼測(cè)試

在JavaScript中執(zhí)行異步代碼是很常見(jiàn)的。 當(dāng)你有以異步方式運(yùn)行的代碼時(shí),Jest 需要知道當(dāng)前它測(cè)試的代碼是否已執(zhí)行完成,然后它可以轉(zhuǎn)移到另一個(gè)測(cè)試。 Jest有若干方法處理這種情況。

回調(diào)

最常見(jiàn)的異步模式就是回調(diào)函數(shù),例如下面的setTimeout方法,下面的測(cè)試用例無(wú)法通過(guò),原因是Jest無(wú)法知道callback具體的調(diào)用時(shí)間,所以會(huì)造成測(cè)試已經(jīng)結(jié)束,但是setTimeout的callback還沒(méi)有執(zhí)行。

test('the data is peanut butter', () => {
  function callback(data: string) {
    expect(data).toBe('peanut butter');
  }
  setTimeout(() => {
    callback('peanut butter');
  }, 2000);
});

想要解決上面的問(wèn)題,非常簡(jiǎn)單,很容易就會(huì)聯(lián)想到消息通知機(jī)制,也就是在callback調(diào)用的時(shí)候通知Jest,表示當(dāng)前測(cè)試用例通過(guò),可以跑下一個(gè)測(cè)試。

test方法的第二個(gè)參數(shù)fn,可以添加一個(gè)done參數(shù),done是一個(gè)方法,調(diào)用了done,就是通知Jest測(cè)試完成,當(dāng)然如果你的測(cè)試用例中的done方法始終沒(méi)有執(zhí)行,那么你的測(cè)試也會(huì)失?。ǔ瑫r(shí)),所以最好的方式就是加上try catch。

test('the data is peanut butter', done => {
  function callback(data: string) {
    try {
      expect(data).toBe('peanut butter');
      done();
    } catch (err) {
      done(err);
    }
  }
  setTimeout(() => {
    callback('peanut butter');
  }, 2000);
});

Promise

如果你的代碼使用了Promise, Jest提供了一種更加直接的方式去處理異步測(cè)試。在test第二個(gè)參數(shù)fn中直接返回一個(gè)promise,Jest就會(huì)等待這個(gè)promise達(dá)到resolved狀態(tài),如果達(dá)到了fulfilled狀態(tài),測(cè)試將會(huì)自動(dòng)失敗。

例如這個(gè)案例,此測(cè)試用例能夠正常的通過(guò)

test('promise resolved', () => {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve('resolved');
    }, 2000);
  }).then((data: string) => {
    expect(data).toBe('resolved');
  });
});

如果promise fulfilled如下,則測(cè)試用例會(huì)跑失敗

test('promise fulfilled', () => {
  return Promise.reject('fulfilled').then((data: string) => {
    expect(data).toBe('fulfilled');
  })
});

當(dāng)然我們也可以使用catch方法,例如下面這個(gè)例子,測(cè)試用例就能夠正常的通過(guò)。

test('promise fulfilled', () => {
  expect.assertions(1);
  return Promise.reject('fulfilled').catch(err => {
    expect(err).toMatch('fulfilled');
  });
});

promise代碼可以配合匹配器.resolvesrejects一起使用,使用案例如下:

test('promise resolved', () => {
  return expect(Promise.resolve('resolved')).resolves.toBe('resolved');
});
test('promise fulfilled', () => {
  return expect(Promise.reject('fulfilled')).rejects.toMatch('fulfilled');
});

Async/Await

如果你的代碼使用了Promise, Jest提供了一種更加直接的方式去處理異步測(cè)試。在test第二個(gè)參數(shù)fn中直接返回一個(gè)promise,Jest就會(huì)等待這個(gè)promise達(dá)到resolved狀態(tài),如果達(dá)到了fulfilled狀態(tài),測(cè)試將會(huì)自動(dòng)失敗。

const TEN = 10;
const BASE = 5;
function fetchData () {
  return new Promise((resolve, reject) => {
    const random = Math.random() * TEN;
    random > BASE ? resolve(random) : reject(random);
  });
}
test('the random promise', async () => {
  expect.assertions(1);
  try {
    const random = await fetchData();
    expect(random).toBeGreaterThan(BASE);
  } catch (e) {
    expect(e).toBeLessThanOrEqual(BASE);
  }
});

Mock Functions

Mock 函數(shù)簡(jiǎn)單的說(shuō)就是模擬一個(gè)函數(shù),這個(gè)功能很強(qiáng)大,例如nodejs中沒(méi)有DOM/BOM,及時(shí)是jsdom也會(huì)缺少一些api,那么我們可以使用mock函數(shù)來(lái)進(jìn)行一些測(cè)試,具體暫不詳細(xì)說(shuō)明。

有兩種方法可以模擬函數(shù):要么在測(cè)試代碼中創(chuàng)建一個(gè) mock 函數(shù),要么編寫(xiě)一個(gè)手動(dòng) mock來(lái)覆蓋模塊依賴。

Mock Functions Doc

使用 mock 函數(shù)

假設(shè)我們要測(cè)試函數(shù) forEach 的內(nèi)部實(shí)現(xiàn),這個(gè)函數(shù)為傳入的數(shù)組中的每個(gè)元素調(diào)用一次回調(diào)函數(shù)。

function forEach(items, callback) {
  for (let index = 0; index < items.length; index++) {
    callback(items[index]);
  }
}

為了測(cè)試此函數(shù),我們可以使用一個(gè) mock 函數(shù),然后檢查 mock 函數(shù)的狀態(tài)來(lái)確?;卣{(diào)函數(shù)如期調(diào)用。

const mockCallback = jest.fn(x => 42 + x);
forEach([0, 1], mockCallback);
// 此 mock 函數(shù)被調(diào)用了兩次
expect(mockCallback.mock.calls.length).toBe(2);
// 第一次調(diào)用函數(shù)時(shí)的第一個(gè)參數(shù)是 0
expect(mockCallback.mock.calls[0][0]).toBe(0);
// 第二次調(diào)用函數(shù)時(shí)的第一個(gè)參數(shù)是 1
expect(mockCallback.mock.calls[1][0]).toBe(1);
// 第一次函數(shù)調(diào)用的返回值是 42
expect(mockCallback.mock.results[0].value).toBe(42);

.mock 屬性

所有的 mock 函數(shù)都有這個(gè)特殊的 .mock屬性,它保存了關(guān)于此函數(shù)如何被調(diào)用、調(diào)用時(shí)的返回值的信息。 .mock 屬性還追蹤每次調(diào)用時(shí) this的值,所以我們同樣可以也檢視

Mock 的返回值

const filterTestFn = jest.fn();
// Make the mock return `true` for the first call,
// and `false` for the second call
filterTestFn.mockReturnValueOnce(true).mockReturnValueOnce(false);
const result = [11, 12].filter(num =&gt; filterTestFn(num));
console.log(result);
// &gt; [11]
console.log(filterTestFn.mock.calls);
// &gt; [ [11], [12] ]

Mocking Modules

在Jest單元測(cè)試的真實(shí)場(chǎng)景中,會(huì)有很多的數(shù)據(jù)來(lái)自接口,但是Jest并不推薦直接在測(cè)試代碼中去調(diào)用真實(shí)的接口,因?yàn)檫@可能會(huì)讓測(cè)試變得非常緩慢而且脆弱,所以jest.fn().mockResolvedValue提供了mock接口的方式,使用假數(shù)據(jù)進(jìn)行測(cè)試。

test('async test', async () =&gt; {
  const asyncMock = jest.fn().mockResolvedValue(23);
  const result = await asyncMock(); // 43
  expect(result).toBe(23);
});

快照測(cè)試

每當(dāng)你想要確保你的UI不會(huì)有意外的改變,快照測(cè)試是非常有用的工具。

一點(diǎn)典型的快照測(cè)試案例就是一個(gè)移動(dòng)端的app渲染一個(gè)UI組件,拍下快照,然后將其與之前測(cè)試存儲(chǔ)的參考快照文件進(jìn)行對(duì)比,如果2個(gè)快照不匹配的話測(cè)試就會(huì)失敗。簡(jiǎn)單的來(lái)說(shuō)就是對(duì)比前后2次組件渲染的照片,這個(gè)測(cè)試方法非常適合React這類UI框架。

Jest快照測(cè)試第一次會(huì)生成一個(gè)快照文件,就像下面這樣

// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`component--Loading 單元測(cè)試start 1`] = `
<div
  className="container"
>
  <div
    className="inner"
  >
    <div
      className="boxLoading"
    />
    <div
      className="title"
    >
      努力加載中...
    </div>
  </div>
</div>
`;

我們可以在Jest 命令中加入--updateSnapshot,這樣快照有跟新的話會(huì)跟新快照文件而不是直接讓整個(gè)快照測(cè)試失敗了。

建議把快照測(cè)試文件提交到git,快照測(cè)試文件也可以進(jìn)行code-review。

以上就是前端單元測(cè)試之UI測(cè)試功能性代碼測(cè)試教程的詳細(xì)內(nèi)容,更多關(guān)于前端單元測(cè)試的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論