Vue使用jest單元測試詳解

文章首發原文地址javascript

工欲善其事必先利其器,使用 jest 作 vue 單元測試前,首先的瞭解什麼是 jest。css

Jest 是一個由 Facebook 開發的測試運行器,致力於提供一個「bettery-included」單元測試解決方案。你能夠在其官方文檔學習到更多 Jest 的知識。html

1、搭建環境

建議使用 vue-cli3 腳手架,搭建 vue 環境,並使用 vue 建立一個 demo 環境vue

npm install -g @vue/cli-service-global
vue create test-demo

//安裝依賴

//插件的方式引入(建議使用此種方式)
vue add @vue/unit-jest
yarn add --dev babel-core@^7.0.0-bridge.0

//或者自定義安裝
yarn add --dev @babel/preset-env jest babel-jest vue-jest @vue/test-utils babel-core@^7.0.0-bridge.0 jest-transform-stub jest-html-reporter jest-serializer-vue jest-environment-jsdom-fifteen

複製代碼

其實使用@vue/unit-jest的方式安裝 jest 測試環境,也是講上面的babel-jest,vue-jest等依賴統一安裝。此處我使用的自定義依賴安裝方式以便於更好的介紹。java

依賴說明:node

  • @babel/preset-env es6 語法轉換
  • jest: facebook 測試框架,必需品
  • babel-jest: 測試文件默認使用 require 方式引用模塊,若是要使用 es6 語法 Import 則須要將代碼轉換成 es5 語法。
  • vue-jest: 告訴 Jest 如何處理 *.vue 文件,咱們須要安裝和配置 vue-jest 預處理器;
  • @vue/test-utils: 使用 vue-test-utils 測試框架測試。
  • babel-core: 要在測試文件中使用 es6 語法,須要使用到 babel-core 轉換代碼,注意點:這裏的 babel-core 必定是 7.0.0 版本以上的,好比 7.0.0-bridge.0,不少狀況下咱們有大量的依賴庫,致使 babel-core 的版本下降到 7.0.0 如下,因此咱們必需要知道 core 的版本。
  • jest-transform-stub:轉換文件
  • jest-html-reporter: 【可選】代碼測試結果展現
  • jest-serializer-vue: 作快照測試的咯
  • jest-environment-jsdom-fifteen: 【可選】js dom 操做
  • jest-watch-typeahead: 【可選】

若是使用自定義安裝方式,請記得將 babel.config.js 修改以下webpack

module.exports = {
  presets: ["@vue/app", "@babel/preset-env"]
};
複製代碼

2、jest 環境配置

若是你不是使用的vue add @vue/unit-jest的方式構建 jest 環境,請查看以下配置,不然請忽略!git

此處只是簡單介紹我在項目開發中使用到的配置,若是有興趣請見官方網址。 等上述工程依賴安裝完成以後,請查閱項目根目錄使用有一個jest.config.js文件,若是不存在建議在根目錄下單首創建一個。es6

jest.config.js說明github

module.exports = {
  //每一個測試腳本都會執行的入口文件,經常咱們在這裏作一些初始化工做,好比可能項目中引用了一些ui庫。
  setupFiles: ["<rootDir>/test/setup"],
  //告訴jest處理那些文件,須要注意,測試vue文件,確定的加上vue
  moduleFileExtensions: ["js", "vue", "jsx", "json"],
  //別名,相似於webpack中的alias,可本身定義一些別名,方便引入庫
  moduleNameMapper: {
    "^@/(.*)$": "<rootDir>/src/$1",
    "^@components/(.*)$": "<rootDir>/src/components/$1",
    "^@plugins": "<rootDir>/test/utils/plugins",
    "^@test": "<rootDir>/test"
  },
  //代碼轉換配置,此處配置的js/jsx文件使用babel-jest轉換成es5
  transform: {
    "^.+\\.(js|jsx)$": "babel-jest",
    "^.+\\.vue$": "vue-jest", //使用vue-jest轉換vue代碼
    ".+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$":
      "jest-transform-stub"
  },
  testEnvironment: "jest-environment-jsdom-fifteen",

  //快照測試時使用jest-serializer-vue,
  snapshotSerializers: ["jest-serializer-vue"],
  //jest作單元測試結果展現,通常狀況測試結果會在控制檯展現出來,若是須要以html的方式展現,能夠
  //安裝jest-html-reporter,或者 majestic進行結果展現
  //如何自定義展現結果,請見第三章
  reporters: [
    "default",
    [
      "./node_modules/jest-html-reporter",
      {
        pageTitle: "DemoTest",
        includeFailureMsg: true,
        outputPath: "./test-report.html",
        includeConsoleLog: true
      }
    ]
  ],
  //此處很重要,見下文介紹
  transformIgnorePatterns: ["/node_modules/", "/node_modules/(?!(你的ui庫))"],

  //正則匹配那些文件須要測試
  testMatch: [
    "**/src/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)",
    "**/test/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)"
  ],

  testURL: "http://localhost/",

  //監聽測試文件插件,也能夠自定義哦
  watchPlugins: [
    "jest-watch-typeahead/filename",
    "jest-watch-typeahead/testname"
  ],
  //是否開啓代碼測試覆蓋率【建議開啓】
  collectCoverage: true,
  coverageDirectory: "coverage",
  coverageReporters: ["json", "lcov", "text", "clover"],
  //此處必需要添加進行代碼測試覆蓋率時忽略那些文件
  coveragePathIgnorePatterns: ["/node_modules/", "package.json", "yarn.lock"]
};
複製代碼

transformIgnorePatterns 配置說明:

默認狀況下jest不會轉換任何/node_modules中的代碼,因爲 jest 在 node 中運行,所以咱們也沒必要轉換任何使用現代 ECMAScript 功能的東西,由於 Node> = 8 已經支持這些功能。 可是在一些狀況下,咱們確實須要轉換 node_modules 中的代碼。

  • typescript 代碼
  • 單文件組件(.vue)經過 vue-jest 運行
  • 使用 es 的import/export 編譯成module.export
  • 你有依賴的 ui 庫,而且使用 es6 語法編寫

針對上述四種狀況,能夠看出,若是你在單元測試文件中有使用到自定義 ui 庫的話,則必須告訴 jest,須要編譯轉換node_modules中的那些代碼。此處的例子只是介紹了/node_modules/(?!(你的ui庫))

3、自定義 jest 測試結果

默認狀況下 jest 會將展現結果展現在控制檯中,那麼咱們如何自定義 jest 的展現結果呢?請見下文。 其實 jest 在執行完成單元測試以後會將測試結果以對象的方式返回。那麼咱們如何獲取到測試結果,官方文檔提供了一個口子,就是reports配置。

也許不少朋友會說,jest 單元測試支持直接在構建時使用-json 的方式直接獲取,可是等你使用 reports 方式獲取時,你會發現 Json 結構是不同的。

自定義結果收集

// 自定義報告文件
class MyCustomReporter {
  constructor(globalConfig, options) {
    this._globalConfig = globalConfig;
    this._options = options;
  }

  /** **咱們能夠在測試完成以後使用results獲取到測試結果 */
  onRunComplete(contexts, results) {
    console.log("jest配置: ", this._globalConfig);
    console.log("額外的慘: ", this._options);
    //經過results能夠將對象已html/其餘方式展現,就看你本身 咯。
  }
}

module.exports = MyCustomReporter;
複製代碼

4、使用 vue-test-utils 進行 vue 測試

前面已經講述了 vue 和 jest 環境配置,若是沒看的朋友建議先看一下。 那麼爲何咱們要使用vue-test-utils進行單元測試呢?引用官方的一句話Vue Test Utils 是 Vue.js 官方的單元測試實用工具庫。

若是不瞭解 Vue Test Utils 的話,建議先去官網學一下。

此處根據 vue 官方提供的例子,簡單介紹幾個測試用例的編寫,具體的測試用例還得根據業務場景去測試。

測試點擊事件

import { shallowMount } from "@vue/test-utils";
import MessageToggle from "@/components/MessageToggle.vue";
import Message from "@/components/Message.vue";

describe("MessageToggle.vue", () => {
  it("toggles msg passed to Message when button is clicked", () => {
    const wrapper = shallowMount(MessageToggle);
    const button = wrapper.find("#toggle-message");
    button.trigger("click");
    const MessageComponent = wrapper.find(Message);
    expect(MessageComponent.props()).toEqual({ msg: "message" });
    button.trigger("click");
    expect(MessageComponent.props()).toEqual({ msg: "toggled message" });
  });
});
複製代碼

快照測試

首先咱們得明白快照測試的意義,快照測試會將上一次運行(若是沒有使用命令更新快照的話)的結果(html)保存一份,以供和下一次單元測試結果進行對比,查看結果(html)是否相同。

注意理解:此處的和前一次對比,並非和前面代碼的toMatchSnapshot結果對比,而是上一次執行單元測試的結果。

import { shallowMount } from "@vue/test-utils";
import List from "@/components/List.vue";

describe("List.vue", () => {
  it("renders li for each item in props.items", () => {
    const items = ["1", "2"];
    const wrapper = shallowMount(List, {
      propsData: { items }
    });
    expect(wrapper.findAll("li")).toHaveLength(items.length);
  });

  it("matches snapshot", () => {
    const items = ["item 1", "item 2"];
    const wrapper = shallowMount(List, {
      propsData: { items }
    });
    //快照測試
    expect(wrapper.html()).toMatchSnapshot();
  });
});
複製代碼

插槽測試

//源文件child2.vue

<template>
  <div>
    <span>哈哈哈哈我是child</span>
    <slot name="aa"></slot>
  </div>
</template>

<script> export default { data() { return { data: { name: "插槽測試-----" } }; } }; </script>

<style></style>
複製代碼
import { mount } from "@vue/test-utils";
import Child from "@/components/child2.vue";

describe("Child", () => {
  it("插槽測試", () => {
    const wrapper = mount(Child, {
      slots: {
        //此處aa對應的是插槽的名字
        aa: ` <div>啦啦啦,我是插槽的數據</div>`
      }
    });

    expect(wrapper.html()).toMatchSnapshot();
  });
});
複製代碼

做用域插槽測試

//源文件child.vue

<template>
  <div>
    <span>哈哈哈哈我是child</span>
    <slot name="ab" :bb="data"></slot>
  </div>
</template>

<script> export default { data() { return { data: { name: "測試-----" } }; } }; </script>

<style></style>
複製代碼
//測試用例child.spec.js

import { mount } from "@vue/test-utils";
import Child from "@/components/child.vue";

describe("Child", () => {
  it("做用域插槽測試", () => {
    const wrapper = mount(Child, {
      scopedSlots: {
        //此處ab對應的是插槽的名字
        ab: ` <div slot-scope="data">{{data.bb.name}}啦啦1</div>`
      }
    });

    expect(wrapper.html()).toMatchSnapshot();
  });
});
複製代碼

最後,感謝你們的閱讀,若是對您有幫助,請記得點個贊哦 原文地址

相關文章
相關標籤/搜索