单元测试
编写单元测试是为了验证小的、独立的代码单元是否按预期工作。一个单元测试通常覆盖一个单个函数、类、组合式函数或模块。单元测试侧重于逻辑上的正确性,只关注应用整体功能的一小部分
快速开始
1. 安装依赖
vue add @vue/cli-plugin-unit-jest
安装完成后,项目自动生成如下文件
- tests 目录是自动化测试的工作区, 可编写单元测试等
- jest.config.js 文件时配置jest的测试环境、es6语法转化、覆盖率报告等
package.json自动生成如下内容
2. jest配置
module.exports = {
// ---- 必备 STRAT ----
// 提供了jest默认配置,可通过路径node_modules/@vue/cli-plugin-unit-jest/presets/default/jest-preset.js找到该默认配置
preset: "@vue/cli-plugin-unit-jest",
// 多于一个测试文件运行时展示每个测试用例测试通过情况,默认多于一个测试文件时不展示;
verbose: true,
// 参数指定只要有一个测试用例没有通过,就停止执行后面的测试用例,默认值为0
bail: true,
// jsdom可以让js在node环境运行
testEnvironment: 'jsdom',
// 匹配所有源文件路径的regexp模式字符串数组,匹配的文件将跳过转换,转译时忽略 node_modules
// transformIgnorePatterns: ['/node_modules/'],
transformIgnorePatterns: ['node_modules/(?!axios)'],
// ---- 必备 END ----
// 覆盖率报告,运行测试命令后终端会展示报告结果
collectCoverage: true,
// 需要检测测的文件类型
moduleFileExtensions: [
'js',
'jsx',
'json',
'vue'
],
// 预处理器配置,匹配的文件要经过转译才能被识别,否则会报错
transform: {
'.+\\.(css|styl|less|sass|scss|jpg|jpeg|png|svg|gif|eot|otf|webp|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga|avif)$':
require.resolve('jest-transform-stub'),
'^.+\\.jsx?$': require.resolve('babel-jest')
},
// 从正则表达式到模块名称的映射,和webpack的alisa类似
moduleNameMapper: {
"\\.(css|less|scss|sass)$": "<rootDir>/tests/unit/StyleMock.js",
},
// Jest在快照测试中使用的快照序列化程序模块的路径列表
snapshotSerializers: [
'jest-serializer-vue'
],
// Jest用于检测测试的文件,可以用正则去匹配
testMatch: [
'**/tests/unit/**/*.spec.[jt]s?(x)',
'**/__tests__/*.[jt]s?(x)'
],
// 需要进行收集覆盖率的文件,会依次进行执行符合的文件
collectCoverageFrom: [
'src/views/**/*.{js,vue}',
'!**/node_modules * '
],
// Jest输出覆盖信息文件的目录,运行测试命令会自动生成如下路径的coverage文件
coverageDirectory: "<rootDir>/tests/unit/coverage",
// 覆盖结果的最低阈值设置,如果未达到阈值,jest将返回失败
coverageThreshold: {
global: {
branches: 60,
functions: 80,
lines: 80,
statements: 80,
},
"src/views/materialManage/materialList/index.vue": {
branches: 100,
functions: 100,
lines: 100,
statements: 100,
},
},
// 环境预置配置文件入口
setupFiles: ["<rootDir>/tests/unit/setup/main.setup.js"]
};
3. 执行测试
可以放在 test/unit/ 文件下,命名方式如 *.spec.js 或 *.test.js,jest 会识别自动识别类似文件, 运行下面代码启动测试脚本
全部执行
npm run test:unit
单个执行
npm run test:unit 文件路径
vue-test-utils
主要负责节点获取,编写测试逻辑,以下是一些常用的api,
1、挂载
- 主要包括mount && shallowMount, 两者都会创建一个包含被挂载和渲染的Vue组件的Wrapper
- mount会渲染整个组件树, 而shallowMount会对子组件存根(不渲染其子组件)
- 配置项详情
import { mount } from '@vue/test-utils'
import Foo from './Foo.vue'
describe('Foo', () => {
it('renders a div', async () => {
const wrapper = mount(Foo);
const wrapper1= shallowMount(Foo);
// classes 返回 Wrapper DOM 节点的 class && 返回 class 名称的数组
wrapper.classes();
wrapper.classes('bar');
// contains 判断 Wrapper 是否包含了一个匹配选择器的元素或组件
wrapper.contains('div');
wrapper.contains(Foo);
// find 返回匹配选择器的第一个 DOM 节点或 Vue 组件的 Wrapper(可以使用任何DOM选择器)
// findAll 返回一个 WrapperArray
wrapper.find('div');
wrapper.find('#foo');
wrapper.findAll('div').at(0); // at索引
// findComponent 返回第一个匹配的 Vue 组件的 Wrapper
// findAllComponents 返回所有匹配的 Vue 组件返回一个 WrapperArray
wrapper.findComponent(Foo);
wrapper.findAllComponents(Foo);
// filter 过滤 WrapperArray,与数组方法相同
const filteredDivArray = wrapper
.findAll('div')
.filter(w => !w.hasClass('filtered'))
// html 返回 Wrapper DOM 节点的 HTML 字符串
// text 返回 Wrapper 的文本内容
wrapper.html();
wrapper.text();
// is 断言 Wrapper DOM 节点或 vm 匹配选择器
wrapper.is('div');
// setData 设置 Wrapper vm 的属性, 通过递归调用 Vue.set 生效
await wrapper.setData({ foo: 'bar' })
// trigger 在该 Wrapper DOM 节点上异步触发一个事件。
await wrapper.trigger('click')
})
})
jest
主要负责对测试结果进行断言,以下是一些常用的断言函数
// 判断是否相等
it('equal', () => {
expect(1 + 2).toBe(3);
expect('a' + 'b').toBe('ab');
expect(['a', 'b']).toEqual(['a', 'b']);
expect({ name: 'xiaoming', age: 18 }).toEqual({ age: 18, name: 'xiaoming' });
});
// 判断类型
it('null', () => {
const n = null;
expect(n).toBeNull(); // 仅匹配 null
expect(n).toBeDefined(); //仅匹配defined
expect(n).not.toBeUndefined(); // 仅匹配undefined
expect(n).not.toBeTruthy(); // 匹配任何 if 语句视为真实的内容
expect(n).toBeFalsy(); // 匹配任何 if 语句视为虚假的内容
});
// 判断大小
it('two plus two', () => {
const value = 2 + 2;
expect(value).toBeGreaterThan(3); // >
expect(value).toBeGreaterThanOrEqual(3.5); // >=
expect(value).toBeLessThan(5); // <
expect(value).toBeLessThanOrEqual(4.5); // <=
expect(value).toBe(4);
expect(value).toEqual(4);
expect(0.1 + 0.2).toBeCloseTo(0.3); // // 浮点数
});
// 判断是否存在
it('there is no I in team', () => {
expect('team').not.toMatch(/I/); // not表示相反
expect('team').toMatch(/Is/);
expect([1,2,3]).toContain('1');
});
端对端测试(e2e)
端到端测试(End-To-End Testing, 简称E2E测试)是一种从头到尾测试整个软件产品以确保应用程序流程按预期运行的技术。它定义了产品的系统依赖性,并确保所有集成部分按预期协同工作。
cypress
https://docs.cypress.io/guides
快速开始
安装
npm install cypress --save-dev
从项目根目录打开Cypress
npx cypress open
添加npm 脚本
{
"scripts": {
"cypress:open": "cypress open"
}
}
编写测试脚本
describe('My First Test', () => {
it('Visits the Kitchen Sink', () => {
// 访问一个页面
cy.visit('https://example.cypress.io');
// 查询元素
cy.contains('type');
// 选择一个元素
cy.get('[data-cy=account]');
// 输入文本
cy.get('[data-cy=account]').type('zhangsan');
// 单击一个元素
cy.contains('type').click();
// 断言
cy.url().should('include', '/commands/actions')
})
})