使用jest對vue項目進行單元測試

最近領導對前端提出了新的要求,要進行單元測試。以前使用vue作了一個快報名小程序的pc端頁面,既然要作單元測試,就準備用這個項目了,以前有些react的經驗,vue仍是第一遭css

vue-cli3.0單元測試方面更加完備,就先升級到了cli3.0,由於項目是用typescript寫的,須要ts-jest,獲得jest的配置以下前端

{
  "jest": {
    "moduleFileExtensions": [
      "js",
      "jsx",
      "json",
      "vue",
      "ts",
      "tsx"
    ],
    "transform": {
      "^.+\\.vue$": "vue-jest",
      ".+\\.(css|styl|less|sass|scss|png|jpg|ttf|woff|woff2)$": "jest-transform-stub",
      "^.+\\.tsx?$": "ts-jest"
    },
    "moduleNameMapper": {
      "^@/(.*)$": "<rootDir>/src/$1"
    },
    "snapshotSerializers": [
      "jest-serializer-vue"
    ],
    "testMatch": [
      "**/tests/unit/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)"
    ],
    "testURL": "http://localhost/"
  }
}

先從簡單的開始,測試了一個正則字符串常量文件,完美,一點問題沒有
而後開始測方案頁面的Scheme.vue組件,這個地方主要就想測一個computed屬性,將三種有表明性的狀況寫完測試案例,興沖沖運行yarn test:unit Scheme.test.ts,結果還不錯,三個it測試用例都經過了,但後面還有一片紅是什麼鬼vue

console.error node_modules/vue/dist/vue.runtime.common.js:589
    [Vue warn]: Invalid prop: type check failed for prop "headerPic". Expected String, got Object.

原來是這個地方調用了一個組件,這個組件須要一個headerPic屬性,用做圖片的src,看源碼node

<SideNav :header-pic="require('../../assets/scheme/schemeSideNavPic.jpg')">

感受沒毛病啊,去vue-devtool,"/img/schemeSideNavPic.f988623b.jpg"是字符串啊,一點毛病沒有,應該不是require的問題啊,應該是require在jest裏面的處理問題,再查看jest配置,已經對jpg等靜態文件作處理了,看了一下jest-transform-stub模塊的源碼,很簡單react

module.exports = {
  process: function() {
    return ''
  }
}

既對這些靜態文件返回空字符串,不作處理,這不就更不該該了呀,幸好有vscode這款利器,能夠方便調試源碼,使用vscode調試沒有報錯,也沒能讓調試器進入vue文件,沒辦法,在ts文件裏const pic = require('../../../assets/scheme/schemeSideNavPic.jpg'),再次調試,發現webpack

clipboard.png
正是jest-transform-stub的內容,確實是個對象,跟在命令行內運行結果一致,也就是說只須要一直處理方式讓其返回爲ios

module.exports = ""

查看jest官網,搜了一下css,運氣不錯😂, 處理靜態文件,moduleNameMapper選項徹底能夠知足需求啊,web

"moduleNameMapper": {
      "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/__mocks__/fileMock.js"
    }

fileMock.js內容vue-cli

// __mocks__/fileMock.js

module.exports = 'test-file-stub';

就是說只要返回字符串就OK了,加上moduleNameMapper,測試完美的跑通了typescript

接下來對Scheme.vue組件發起模擬點擊測試

const createScheme = wrapper.findAll('.sn-item').at(1)
createScheme.trigger('click')
expect((wrapper.vm as any).isCreateDialogShow).toBeTruthy()
expect(wrapper.find('.create-list-dialog').isVisible()).toBeTruthy()

使用vue-test-utils的api獲取createScheme元素,對其觸發點擊,測試isCreateDialogShow這個data值被設置成true, 使用的element-ui

<el-dialog
      :visible.sync="isCreateDialogShow"
      width="600px"
      class="create-list-dialog"
      title="建立方案">
      ...
</el-dialog>

此dialog可見,順利經過

接下來再實驗一下新功能,快照,使用toMatchSnapshot方法也順利經過了

接下來來個大的,測試一下Login.vue,登錄頁面,主要測其調接口,而後成功設置store值,但不能走真實的網絡接口啊,這太慢不說,具體結果還不能預測,得使用mock數據
在項目中建立了axios.plugin.ts vue插件,這可怎麼mock呀,再看官方文檔,感受Manual Mocks部分最合適,可是舉例也不適合vue 插件mock啊,繼續瀏覽網站,不知道是受哪的啓發仍是忽然開竅了,應該是受fs模塊啓發,忽然知道怎麼mock插件了,mock一個模塊只須要模仿其型便可,具體實現,就無所謂了,這個http請求插件的mock必須能返回咱們指望的值啊,fs模塊的__setMockFiles又給了我啓示,能夠直接給接口的返回result設值啊,而後就有來下面的__mocks__/axios.plugin.ts文件

const MockAxios = {} as any

let result = {} as any
MockAxios.install = (Vue: any, options: any) => {
  Vue.prototype.$axios = function () {
    /* eslint-disable prefer-promise-reject-errors */
    return new Promise((resolve, reject) => {
      if (result.ResultCode === '200') {
        return result.Info
      } else {
        reject({ code: result.ResultCode, msg: result.Message, info: result.Info })
      }
    })
  }
}

MockAxios.__setMockData = (data: any) => {
  result = data
}

export default MockAxios

而後一馬平川了,localVue.use(Vuex), localVue.use(AxiosPlugin)

const mockData = {
  ResultCode: '200',
  Msg: true,
  Info: {
    OpenId: 99,
    UserId: 92003,
  },
}
AxiosPlugin.__setMockData(mockData)
(wrapper.vm as any).login({ code: '29992' }).then(() => {
  expect(wrapper.vm.$store.state.userInfo.OpenId).toBe(mockData.Info.OpenId)
  expect(wrapper.vm.$store.state.userInfo.UserId).toBe(mockData.Info.UserId)
})

完美經過,vue的單元測試框架算是基本搭好了,也能給領導說說了

給領導看還得有個覆蓋率報告
yarn test:unit --coverage
覆蓋的文件比較少啊,不包含全部的源文件啊,須要加入collectCoverageFrom配置項,至此整個單元測試就比較完備了
下面是完整jest的配置

{
  "jest": {
    "moduleFileExtensions": [
      "js",
      "jsx",
      "json",
      "vue",
      "ts",
      "tsx"
    ],
    "transform": {
      "^.+\\.vue$": "vue-jest",
      ".+\\.(css|styl|less|sass|scss|png|jpg|ttf|woff|woff2)$": "jest-transform-stub",
      "^.+\\.tsx?$": "ts-jest"
    },
    "moduleNameMapper": {
      "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/__mocks__/fileMock.js",
      "^@/(.*)$": "<rootDir>/src/$1"
    },
    "snapshotSerializers": [
      "jest-serializer-vue"
    ],
    "testMatch": [
      "**/tests/unit/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)"
    ],
    "testURL": "http://localhost/",
    "collectCoverageFrom": [
      "**/*.{vue,ts}",
      "!**/node_modules/**",
      "!**/*.d.ts"
    ]
  }
}
相關文章
相關標籤/搜索