vue-cli jest 接入指南

安裝vue-cli

vue-test-utilsVue 官方的測試庫,並將在本指南中貫穿始終。它在瀏覽器和Node.js環境中皆可運行,並能配合任何 test runner 使用。在本指南中,咱們將在 Node.js 環境運行測試。javascript

vue-cli 是起步的最簡單方式。它將創建一個項目,也會配置 Jest,一個流行的測試框架。其安裝方法是:css

yarn global add @vue/cli
複製代碼

或經過 npm:html

npm install -g @vue/cli
複製代碼

經過運行 vue create [project-name] 來建立一個新項目。選擇 "Manually select features" "Unit Testing",以及 "Jest" 做爲 test runner。vue

一旦安裝完成,cd 進入項目目錄中並運行 yarn test:unit。若是一切順利,你將看到:java

PASS  tests/unit/HelloWorld.spec.js
  HelloWorld.vue
    ✓ renders props.msg when passed (26ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        2.074s
複製代碼

恭喜,你已經運行了你的第一個經過的測試!node

安裝@vue/cli-plugin-unit-jest

vue add @vue/cli-plugin-unit-jest
複製代碼

若是你的本地依賴的vue和你全局的vue版本不同你須要升級。git

update-vue.jpeg 安裝完成後你會發現本地多了一些文件github

7filesChange.png

jest.config.js

jest的配置文件就是這個,他會有默認配置。具體要改什麼,須要的時候查一下,若是基礎版能夠無論。web

jestconfig.png 逐一註釋講解配置的做用:正則表達式

const path = require('path')
  module.exports = {
    preset: '@vue/cli-plugin-unit-jest/presets/typescript-and-babel',
    // rootDir:其實就是指整個項目的根目錄,也就是最外層的目錄。這裏多句嘴,再解釋下path.resolve(__dirname,"../../")的意義,他最終返回的結果是該問見所在的根目錄,簡單來講__dirname返回的是當前目錄,再向上兩層,就是整個項目的根目錄了。
    rootDir: path.resolve(__dirname, './'),
    testMatch: ['**/test/unit/**/*.(spec|test).[jt]s?(x)', '**/__test__/**/*.(spec|test).[jt]s?(x)'],
    // moduleNameMapper:一種正則表達式到模塊名的映射,匹配到的文件的內容能夠是空的。我理解的是,能夠經過該參數,來mock一些圖片,css等靜態資源文件,由於咱們在測試的時候其實是不太須要這些文件的,可是有須要引入它做爲環境上的依賴。
    moduleNameMapper: {
       '^@esign-ui/base/(.*)$': '<rootDir>/packages/base/$1',
       '@esign-ui/biz/(.*)$': '<rootDir>/packages/biz/$1',
       '@esign-ui/shared/(.*)$': '<rootDir>/packages/shared/$1',
       '@esign-ui/helpers/(.*)$': '<rootDir>/packages/helpers/$1',
     },
     // transform:簡單來講就是轉換器,正則匹配到的文件能夠經過對應模塊的轉換器來解決一些將來版本語法時可使用它。經過正則來匹配文件,爲匹配到的文件使用對應的模塊。
     transform: {
       '^.+\\.js$': '<rootDir>/node_modules/babel-jest',
       '.*\\.(vue)$': '<rootDir>/node_modules/vue-jest',
       '\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': '<rootDir>/test/unit/fileTransformer.js',
     },
     // snapshotSerializers:快照測試的插件,會生成測試文件的一個快照版本,能夠再package.json中查看安裝的快照插件。
     snapshotSerializers: [<rootDir>/node_modules/jest-serializer-vue],
     // setupFiles:運行一些測試環境所要依賴的模塊的路徑列表,好比引入vue,elementUI等插件的列表,以給測試提供完整的環境。
     setupFiles: ['jest-canvas-mock', '<rootDir>/test/unit/setup'],
     // collectCoverage:是否收集測試時的覆蓋率信息。
     collectCoverage: true,
     // coverageDirectory:jest輸出覆蓋率信息文件的目錄。
     coverageDirectory: '<rootDir>/test/unit/coverage',

     // collectCoverageFrom:爲數組中匹配的文件收集覆蓋率信息,即便並無爲該文件寫相關的測試代碼,須要將collectCoverage設置爲true,或者經過--corverage參數來調用jest。
     collectCoverageFrom: [
       // 覆蓋目錄 
       '<rootDir>/src/**/*/*.{js,vue,jsx,tsx,ts}',
       // 不覆蓋目錄
       '!**/node_modules/**',
     ],
  }
複製代碼

此時package.json多出來了兩個腳本命令,一個是lint檢查,一個就是咱們將要使用的單元測試腳本了。

2scripts.png

yarn test:unit
或者
npm run test:unit
複製代碼

增長test文件夾

根目錄增長了test文件夾,test下面又有unit文件夾,下面就是單元測試demo文件example.spec.ts。具體的單元測試用例就寫在這裏。

// 簡單的用例
import { shallowMount } from '@vue/test-utils'
import HelloWorld from '@/components/HelloWorld.vue'

describe('HelloWorld.vue', () => {
  it('renders props.msg when passed', () => {
    const msg = 'new message'
    const wrapper = shallowMount(HelloWorld, {
      propsData: { msg }
    })
    expect(wrapper.text()).toMatch(msg)
  })
})
複製代碼

單測的測試範圍

經過 Jest 運行單元測試。默認的 testMatch<rootDir>/(test/unit/**/*.(spec|test).(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx))

匹配:任何test/unit 中以 .spec.(js|jsx|ts|tsx) 結尾的文件;任何 __tests__ 目錄中的 js(x)/ts(x) 文件。 使用:vue-cli-service test:unit [options] <regexForTestFiles>支持全部的 Jest 命令行選項

安裝過程遇到的問題

1.未安裝babel-jest

babel-jestInstall.png

修復方法: npm i -D babel-jest or yarn add babel-jest -D

2.版本不支持

errorScene1.png

修復方法: 將babel-jest版本調整到24.0.0-25.0.0之間就行了。

3.須要注入store/vuex/vue-router等一類的報錯

要注入store.png

修復方法:須要將store/vuex/vue-router等用到的插件mock掉。

4.element-ui 組建的事件沒法解析

lALPDhYBMmiG_1bNAtjNBAA_1024_728.png_720x720g.jpg

修復方法:input事件是input組建emit出來的,須要本身模擬hack掉,不能直接觸發。

單測覆蓋率指標

unitTest.png %stmts是語句覆蓋率(statement coverage):是否是每一個語句都執行了? %Branch分支覆蓋率(branch coverage):是否是每一個if代碼塊都執行了? %Funcs函數覆蓋率(function coverage):是否是每一個函數都調用了? %Lines行覆蓋率(line coverage):是否是每一行都執行了?

經常使用簡單的jest語法和使用方式

  1. describe 用來描述一個測試用例的做用和細節
describe('填寫頁面的測試用例', () => {
    // ... 測試用例
  })
複製代碼
  1. it

jest的測試基礎單元

it('unit test describe',() => {
    //...測試的主體內容
  })
複製代碼
  1. expect

Jest爲咱們提供了expect函數用來包裝被測試的方法並返回一個對象,該對象中包含一系列的匹配器來讓咱們更方便的進行斷言。

test('2+2=4', () => {
    expect(2 + 2).toBe(4);
  });
複製代碼

經常使用的幾個斷言(匹配器)

  1. .not
test('the best flavor is not coconut', () => {
    expect(bestLaCroixFlavor()).not.toBe('coconut');
  });
複製代碼
  1. .toBe()
const can = {
    name: 'pamplemousse',
    ounces: 12,
  };

  describe('the can', () => {
    test('has 12 ounces', () => {
      expect(can.ounces).toBe(12);
    });

    test('has a sophisticated name', () => {
      expect(can.name).toBe('pamplemousse');
    });
  });
複製代碼
  1. .toEqual()
const can1 = {
    flavor: 'grapefruit',
    ounces: 12,
  };
  const can2 = {
    flavor: 'grapefruit',
    ounces: 12,
  };

  describe('the La Croix cans on my desk', () => {
    test('have all the same properties', () => {
      expect(can1).toEqual(can2);
    });
    test('are not the exact same can', () => {
      expect(can1).not.toBe(can2);
    });
  });
複製代碼
  1. .toHaveLength()
expect([1, 2, 3]).toHaveLength(3);
  expect('abc').toHaveLength(3);
  expect('').not.toHaveLength(5);
複製代碼
  1. .toThrow()
test('throws on octopus', () => {
    expect(() => {
      drinkFlavor('octopus');
    }).toThrow();
  });
複製代碼
  1. .toMatch()
describe('an essay on the best flavor', () => {
    test('mentions grapefruit', () => {
      expect(essayOnTheBestFlavor()).toMatch(/grapefruit/);
      expect(essayOnTheBestFlavor()).toMatch(new RegExp('grapefruit'));
    });
  });

  describe('grapefruits are healthy', () => {
    test('grapefruits are a fruit', () => {
      expect('grapefruits').toMatch('fruit');
    });
  });
複製代碼
  1. expect.assertions(number) 期待斷言的次數

shallowMount和mount的區別(重點)

mount和shallwMount.png

shallowMount和mount的結果是個被封裝的Wrapper,能夠進行多種操做,譬如find()、parents()、children()等選擇器進行元素查找;state()、props()進行數據查找,setState()、setprops()操做數據;simulate()模擬事件觸發。shallowMount只渲染當前組件,只能能對當前組件作斷言;mount會渲染當前組件以及全部子組件,對全部子組件也能夠作上述操做。通常交互測試都會關心到子組件,我使用的都是mount。可是mount耗時更長,內存啥的也都佔用的更多,若是不必操做和斷言子組件,可使用shallowMount。

mock

WechatIMG11864.png

相似這樣的公共函數,咱們在寫組件的單測時組件內部會用到,因此咱們就要將這些函數給mock掉,避免公共函數的加載或者依賴帶來的報錯或者解析問題。

jest.mock('@/lib/http/will', ()=>{
 rerurn {
   pwdAuth(xxx) {
     return new Promise(resolve, reject) {
       if(xxx) {
         resolve(xxx)
       } else {
         reject()
       }
     }
   }
 }
})
複製代碼

但願對你們有所幫助。 送上我常常看的jest的文檔地址 jest的文檔:jestjs.io/docs/zh-Han… Vue測試指南: lmiller1990.github.io/vue-testing…

相關文章
相關標籤/搜索