文章首发原文地址javascript
工欲善其事必先利其器,使用 jest 作 vue 单元测试前,首先的了解什么是 jest。css
Jest 是一个由 Facebook 开发的测试运行器,致力于提供一个“bettery-included”单元测试解决方案。你能够在其官方文档学习到更多 Jest 的知识。html
建议使用 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.config.js 修改以下webpack
module.exports = {
presets: ["@vue/app", "@babel/preset-env"]
};
复制代码
若是你不是使用的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 中的代码。
import/export
编译成module.export
针对上述四种状况,能够看出,若是你在单元测试文件中有使用到自定义 ui 库的话,则必须告诉 jest,须要编译转换node_modules
中的那些代码。此处的例子只是介绍了/node_modules/(?!(你的ui库))
。
默认状况下 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;
复制代码
前面已经讲述了 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();
});
});
复制代码
最后,感谢你们的阅读,若是对您有帮助,请记得点个赞哦 原文地址