天天看點

單元測試(三)—— Jest Mock元件方法及axios promise請求前言Coverage 測試覆寫率報告模拟Promise請求Mock方法總結

文章目錄

  • 前言
  • Coverage 測試覆寫率報告
  • 模拟Promise請求
  • Mock方法
  • 總結

前言

這篇部落格我們主要講三個方面的東西,在元件中的方法的

mock

,以及對元件中

promise

這類異步請求的

mock

,在中間我們會穿插一下對測試覆寫率報告的講解。先說一下我們為什麼需要測試覆寫率報告和對方法以及請求的模拟操作。首先

coverage

的存在讓我們對我們項目的測試程度有一個客觀的認知,它把我們測試用例的覆寫範圍進行了一個量化操作,并且讓我們可以清晰地看到我們的代碼是否被執行,執行了多少次,覆寫了多少個行,方法,if子產品等等,這些都是我們需要參考的名額級的東西。但是我們不能完全依賴測試覆寫率來衡量我們項目是否已經被我們的測試用例完美覆寫,因為這份報告隻要代碼被運作,就會增加我們的覆寫率,簡而言之就是即使我們沒有去斷言,代碼隻要被運作了,測試覆寫率報告也會把這部分計入到覆寫率中,這無疑對于我們來說是一個誤判,我們無法得知我們的代碼是否已經真實地被完美測試了,是以我們到最後還是需要對我們的測試用例有一個主觀的判斷,是否已經達到了我們的測試标準。

關于測試覆寫率報告的具體知識點推薦可以看一下阮一峰老師對于Istanbul這款插件的部落格代碼覆寫率工具 Istanbul 入門教程

接下來我們讨論一下模拟方法的重要性,首先上篇部落格我們已經提到了現在的測試對象依舊是以公共元件為主,這部分的元件相對而言是比較穩定且益于測試的,編寫這方面的測試代碼的成本相對而言不會很高,并且有着不錯的收益,變動的機率也不會很大。但是一旦我們深入到業務元件的測試中,我們就必須需要去模拟我們請求的各種狀态去測試我們的資料流和

DOM

回報是否是一個正常的狀态,這個就需要我們去模拟去研究如何

mock

promise

整個異步操作。

最後是

mock function

,為什麼我們需要對方法進行模拟,因為在我們的項目開發中我們經常會需要一些

callback

也就是在一個方法中對别的方法進行回調操作,但是我們是無法對一個真實的方法是否被調用進行斷言的,是以我們需要先把那個期待被測試的方法優先

mock

掉,這樣就可以去調用

jest

的中的方法去對這個方法是否被斷言是否被調用。

Coverage 測試覆寫率報告

我們執行npm run test:e2e指令運作單元測試後,會得到一個測試覆寫率報告,在第一篇部落格中我們已經在

jest.config.js

中進行過相應配置了,首先會在我們的控制台得到一個指令行式的覆寫率報告,如圖

單元測試(三)—— Jest Mock元件方法及axios promise請求前言Coverage 測試覆寫率報告模拟Promise請求Mock方法總結

這裡我們可以看到每個

spec

都被執行過一次了,如果遇到哪個用例中的斷言不通過則會顯示相應的

it

中的名字,并且把斷言所期望和收到的值都列印出來。

在所有用例都跑過之後,會在我們項目下生成一個

coverage

目錄,裡面會對應我們每一個項目中的元件生成一個

html

檔案來标記我們的代碼執行情況

單元測試(三)—— Jest Mock元件方法及axios promise請求前言Coverage 測試覆寫率報告模拟Promise請求Mock方法總結

我們點選一個元件進去看一下

單元測試(三)—— Jest Mock元件方法及axios promise請求前言Coverage 測試覆寫率報告模拟Promise請求Mock方法總結

我們這裡以StartQlink.vue元件為例,可以看到我們這個元件的方法都已經被執行一遍了,行覆寫率達到了93.62%,并且可以看到我們的方法被執行了幾次。

模拟Promise請求

這是一個非常讓我頭疼的一個點,在mock請求這個點上我花了很久的去研究如何

mock

掉整個

axios

的請求。這個教程還是有一點瑕疵的,不過總體上已經達到了功能上的需求,這裡我主要參考了老外的一個針對React寫的測試教程。Mocking Axios in Jest + Testing Async Functions,想看這篇教程需要科學上網。

接下來我們進入整體,在

mock

整個

axios

的異步請求前,我們主要依賴的方法為

Jest

提供的

jest.fn()

這個函數。

  1. 首先我們需要在tests檔案下建立一個名為

    __mocks__

    的目錄,注意名稱必須是這個,否則無法被

    Jest

    所識别到。
  2. 然後我們建立一個

    axios.ts

    的檔案,在裡面寫入如下代碼
const mockAxios: any = jest.genMockFromModule('axios');

// this is the key to fix the axios.create() undefined error!
mockAxios.create = jest.fn(() => mockAxios);
mockAxios.get = jest.fn(() => Promise.resolve({ data: {} }));
mockAxios.post = jest.fn(() => Promise.resolve({ data: {} }));
mockAxios.put = jest.fn(() => Promise.resolve({ data: {} }));
mockAxios.delete = jest.fn(() => Promise.resolve({ data: {} }));
mockAxios.all = jest.fn(() => Promise.resolve());

export default mockAxios;
                

在這段代碼中我們首先需要mock掉整個

axios

子產品,然後我們需要對

axios

中的

create

方法進行一個

mock

對我們的各種類型的方法進行一個模拟,我們這裡統一采用了

promise

中的

resolve

場景,并且指定一個空的

data

,最後抛出這個模拟的子產品

  1. 模拟請求
import Vue from 'vue'
import { shallowMount } from '@vue/test-utils';

import StartQLink from '@/views/Steps/views/StartQLink/StartQLink.vue'

import mockAxios from '../__mocks__/axios'
import { qlinks } from '../__mocks__/startQlink'

describe('StartQLink.vue', () => {
 it('擷取QLink', () => {
   const wrapper = shallowMount(StartQLink)
   const vm: any = wrapper.vm
   mockAxios.get.mockImplementationOnce(() => {
     return Promise.resolve({
       data: {
         data: qlinks,
         error_code: 0,
         message: '',
       },
     })
   })
   vm.getQlinks()
   Vue.nextTick(() => {
     Vue.nextTick(() => {
       expect(vm.qlinks).toEqual(qlinks)
     })
   })
 })
})
                

這裡我們首先引用我們完成

mock

mockAxios

子產品後,在後面我們引入了我們準備好的模拟資料,由于我們這一次需要mock的是一個

GET

請求,是以在用例中我們通過調用

mockAxios.get.mockImplementationOnce

return

一個

Promise.resolve

傳回我們需要的

response body

,這裡因為我沒有去模拟

reject

以及

catch

情況,是以我們的控制台會有一個warning,這個瑕疵我之後再解決。

  1. 之後我們運作我們包含請求的方法,我們通過

    vm

    執行個體通路元件中的

    function

    ,因為這是兩次異步的操作,在方法調用後還有一次

    promise

    的異步請求,是以我們這裡使用了兩次

    Vue.nextTick

    來包裹我們最終的斷言,在這個方法中我對請求回來的資料直接指派到了

    data

    中,是以我在斷言中直接通路了元件執行個體上的

    qlinks

    字段并且使用了

    toEqual

    方法去判斷是否和我準備的資料相等,可以看到我們上面的測試報告中顯示我們的代碼已經被執行了,并且斷言是一個通過的狀态,說明我們的請求已經被成功模拟了

Mock方法

在上面我們完成了對Promise請求的模拟,我們的需求中還有可能對方法進行回調的斷言,這裡我也示範一下,先上代碼。

it('建立表空間', () => {
    const wrapper = shallowMount(CreateDB)
    const vm: any = wrapper.vm
    const mockFn = jest.fn()
    wrapper.setData({
      dbs: dbsMock,
      currentDB: 'test',
    })
    wrapper.setMethods({
      getTabs: mockFn,
    })
    mockAxios.post.mockImplementationOnce(() => {
      return Promise.resolve({
        data: {
          data: {},
          error_code: 0,
          message: '',
        },
      })
    })
    vm.handlePostCreateTab()
    Vue.nextTick(() => {
      Vue.nextTick(() => {
        expect(mockFn).toHaveBeenCalled()
        expect(vm.openAddTabModal).toBeFalsy()
      })
    })
  })
                

在以上這段代碼中我們使用

const mockFn = jest.fn()

建立了一個假的方法,然後通過

vue-test-utils

中的

setMethods

去替代了原先需要被回調判斷的方法

getTabs

,之後我們在斷言中直接使用

toHaveBeenCalled

判斷這個方法是否被調用就可以了。當然也可以使用其他的方法去判斷這個方法具體被執行了多少次,這裡就不贅述了。

總結

這一篇我們着重講解了測試用例和測試覆寫率報告的結合使用以及對方法和請求的Mock操作,這是測試業務元件和測試用例撰寫的一個非常重要的點,有了這些基礎功能後使得我們對後面元件更深入的測試有了一個更加光明的遐想空間,為提升我們的測試覆寫率以及我們的斷言品質也有了一個非常好的鋪墊。在下一個章節我将會去研究如何測試

setTimeout

以及

Vuex

這類狀态管理庫。

繼續閱讀