〇、前言
集成单元测试的过程还是简单的,难的是用例的书写和补充,所以这篇文档也是在最后记录我在写用例时常用的语法和api以及一些较为复杂场景的用例,共勉😊
一、安装jest
1、基于vue项目的初始化安装配置
-
初始化配置命令
vue add @vue/unit-jest
-
生成一个脚本命令以及对应的依赖
··· "scripts": { ··· "lint": "vue-cli-service lint" }, ··· "devDependencies": { ··· "@vue/cli-plugin-unit-jest": "^4.5.15", ··· },
-
基于初始化配置,可以进行基础的单元测试
// add.js组件文件 export default { template: ` <div> <span class="counts">{{ counts }}</span> <button @click="add">➕</button> </div> `, data() { return { counts: 0 } }, methods: { add() { this.counts++ } } } // addTest.js测试用例文件 import { mount } from '@vue/test-utils' import Add from './add' describe('Add', () => { // 挂载组件 const wrapper = mount(Add) it('初始化存在正确的span元素', () => { expect(wrapper.html()).toContain('<span class="counts">0</span>') }) it('存在一个按钮', () => { expect(wrapper.contains('button')).toBe(true) }) it('模拟用户点击+,counts加1', () => { // 找到button元素 const button = wrapper.findComponent('button') // trigger可以触发元素默认的事件 button.trigger('click') // 断言counts+1 expect(wrapper.vm.counts).toBe(1) }) })
以上是几个简单的用例,较为复杂的放在文档最后面,整理了一些用例场景和一些常用的api,只是以上的初始化配置并不能满足我们的需求,比如生成覆盖率报告文件,实时监听测试用例等等
2、基于vue项目的个性化安装配置
-
安装vue-jest和@vue/test-utils、
jest-serializer-vue快照测试
依赖npm install --save-dev vue-jest @vue/test-utils jest-serializer-vue
-
在文件根目录下面新建一个jest.config.js配置文件
module.exports = { 'preset': '@vue/cli-plugin-unit-jest', 'roots': [ '<rootDir>' // 定义根目录路径 ], 'snapshotSerializers': ['jest-serializer-vue'], // 快照的序列化工具 'moduleFileExtensions': [ 'js', 'json', // 告诉 Jest 处理 `*.vue` 文件 'vue' ], 'moduleNameMapper': { '^@/(.*)$': '<rootDir>/src/$1', // 将@/引入的路径格式映射替换,防止路径出错 '\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': '<rootDir>/tests/unit/__mocks__/fileMock.js', // 模拟加载静态文件 '\\.(css|less|scss|sass)$': '<rootDir>/tests/unit/__mocks__/styleMock.js' // 模拟加载样式文件 }, 'transform': { '.*\\.(vue)$': 'vue-jest' // 用 `vue-jest` 处理 `*.vue` 文件 // 项目解析js文件使用babel-jest存在冲突,先注释了,也能解析,可能是因为初始化生成的配置也可以解析 // '^.+\\.js$': '<rootDir>/node_modules/babel-jest' }, 'coverageDirectory': '<rootDir>/tests/unit/coverage', // 生成覆盖率报告的目录 'testMatch': ['**/tests/unit/specs/*.spec.[jt]s?(x)'], // 匹配测试用例的文件 'transformIgnorePatterns': ['/node_modules/'], // 忽略解析的文件 'collectCoverage': false, // 测试报告想要覆盖那些文件,目录,前面加!是避开这些文件,根据个人项目自己配置 'collectCoverageFrom': [ 'src/**/*.{js,vue}', '!src/main.js', '!src/App.vue', '!**/node_modules/**' ], 'coverageReporters': ['html', 'text-summary'] // 生成覆盖率报告文件类型 };
-
重新设置运行脚本
"scripts": { ··· "unit": "vue-cli-service test:unit", // 执行一次测试,生成通过和总用例数 "unit:watch": "vue-cli-service test:unit --watchAll", // 实时监听测试 "coverage": "vue-cli-service test:unit --coverage", // 生成测试覆盖率报告文件 "verbose": "vue-cli-service test:unit --verbose" // 生成所有用例测试结果 },
-
执行一次测试,在根目录下生成一个tests的文件夹,下面对tests文件下进行配置
// tests/unit/__mocks__/fileMock.js module.exports = 'test-file-stub'; // 模拟输出静态资源 // tests/unit/__mocks__/styleMock.js module.exports = 'test-file-stub'; // 模拟输出静态资源 // tests/unit/.eslintrc { "env": { "jest": true } } // tests/unit/coverage/** `跑用例时自己生成覆盖率报告文件,无需配置` // tests/unit/specs/__snapshots__/*.spec.js.snap `用例文件中使用了快照测试就会生成相对应的快照文件,内容都是一个个简单的dom树` // tests/unit/specs/*.spec.js `测试用例文件,都是以.spec.js后缀结尾创建`
- 因为
tests/unit/coverage
文件夹比较大,切不是必须提交的,在提交时可忽略
// .gitignore node_modules coverage
- 因为
-
至此,配置工作已经完成,下面将我们之前的
addTest
用例运行一下add.js文件
AddTest.spec.js文件
运行之后生成的测试结果,中间爆红是因为目前的toContain方法即将废弃,出现警告
3、常用写法的整理
-
computed用法
// Table.js export default { template: ` <div id="table" v-if="visible">{{ tableData }}</div> `, props: { moduleVisible: { type: Boolean, default: false } }, data() { return { tableData: [] }; }, computed: { visible: { get() { if (this.moduleVisible) { this.getData(); } return this.moduleVisible; }, set() { } } }, methods: { getData() { this.tableData = [1, 2]; } } }; // Table.spec.js import Table from './Table.js'; import { mount, createLocalVue } from '@vue/test-utils'; const localVue = createLocalVue(); describe('Table', () => { test('computed', async() => { const wrapper = mount(Table, { localVue, propsData: { moduleVisible: false } }); expect(wrapper.vm.visible).tobe(false); expect(wrapper.vm.tableData).toEqual([]); // 初始化为空 await wrapper.setProps({ moduleVisible: true }); // 设置为true expect(wrapper.vm.visible).toBe(true); expect(wrapper.vm.getData).toBeTruthy(); // 断言执行函数 expect(wrapper.vm.tableData).toEqual([1, 2]); // 断言变量更改后的值 }); });