当前位置:网站首页>初使jest 单元测试
初使jest 单元测试
2022-08-10 06:51:00 【阿六啊】
前言
最近业务需求不多,所以开始整项目的测试。首先申明项目vue2.0 + ts构建的。
自动化测试
关于自动化测试的定义相关的知识,可以翻阅其他文章。这里只简单的做一下介绍哦。
前端测试主要分为 3 种:单元测试(Unit Test)、集成测试(Integration Test)、UI 测试(UI Test),同时我看到也有很多文章提到了End-to-end(端到端测试)
事实上,UI 测试(UI Test)和端到端测试(E2E Test)是稍有区别的:
UI 测试(UI Test)只是对于前端的测试,是脱离真实后端环境的,仅仅只是将前端放在真实环境中运行,而后端和数据都应该使用 Mock 的。
端到端测试(E2E Test)则是将整个应用放到真实的环境中运行,包括数据在内也是需要使用真实的。
就前端而言,UI 测试(UI Test)更贴近于我们的开发流程。在前后端分离的开发模式中,前端开发通常会使用到 Mock 的服务器和数据。因而我们需要在开发基本完成后进行相应的 UI 测试(UI Test)。
单元测试: 单元测试是测试一个模块,不依赖任何外部资源
集成测试: 测试一个模块或者多个模块,并伴随着它们对应的外部依赖资源,它测试的是应用代码的集成性,比如文件或者数据库。
测试框架
目前市面上比较流行的前端测试框架有Mocha、QUnit、Jasmine、Jest等,以下作个简单介绍
jest基本使用
安装
如果是后期添加单元测试的话,首先要安装Jest和Vue Test Utils:
vue add @vue/cli-plugin-unit-jest
这个命令会帮我们把相关的配置都配好,相关的依赖都装好,还会帮我们生成一个jest.config.js文件。
jest中常用的一些配置项
module.exports = {
preset: '@vue/cli-plugin-unit-jest/presets/typescript-and-babel',
testMatch: ['**/tests/spec/**/**/*.spec.[jt]s?(x)', '**/__tests__/*.[jt]s?(x)'],
setupFiles: ['<rootDir>/tests/setup']
};
(3)在项目目录中创建tests文件,再创建xxxx文件,在其中文件命名的话,就以 xxx.spec.js命名
(4)在package.json 中添加启动命令,然后通过在控制台执行npm run test:unit ,进行测试
Scripts配置:
"test:unit": "vue-cli-service test:unit"
基本语法规则
(1)Jest 支持三种方式写测试代码
- .spec.js
- .test.js
- 放在 tests文件夹下
(2)测试结构
describe: 将几个相关的测试放到一个组中,非必须
test :(别名it)测试用例,是测试的最小单位
expect:提供很多的matcher 来判定你的方法返回值是否符合特定条件
describe('add的方法测试',()=>{
test('2+3应该等于5',()=>{
expect(add(2,3)).toBe(5)
})
})
(3)mock方法和 处理
Jest的mock方式 (Jest.fn()、Jest.spyOn()、Jest.mock())
预处理和后处理
beforeAll / afterAll : 对测试文件中所有的用例开始前/ 后 进行统一的预处理
beforeEach/ afterEach : 在每个用例开始前 / 后 进行预处理
(4)覆盖率指标
在package.json中 设置 --coverage 即可 测试覆盖率
"test:unit": "vue-cli-service test:unit --coverage"
%stmts是语句覆盖率(statement coverage):是不是每个语句都执行了?
%Branch分支覆盖率(branch coverage):是不是每个if代码块都执行了?
%Funcs函数覆盖率(function coverage):是不是每个函数都调用了?
%Lines行覆盖率(line coverage):是不是每一行都执行了?
配置好后,会生成一个coverage文件,然后打开里面的index.html 里面会有详细的信息展示
三种颜色分别代表不同比例的覆盖率(<50%红色,50%~80%灰色, ≥80%绿色)
旁边显示的1x代表执行的次数
80%以下不及格,90%以上可以使用,95%以上优秀
jest基本语法
4.常用的方法
–mount: 创建一个包含被挂载和渲染的 Vue 组件的 wrapper,它仅仅挂载当前实例
—shallowMount:和 mount 一样,创建一个包含被挂载和渲染的 Vue 组件的 Wrapper,只挂载一个组件而不渲染其子组件 (即保留它们的存根),这个方法可以保证你关心的组件在渲染时没有同时将其子组件渲染,避免了子组件可能带来的副作用(比如Http请求等)
—shallowMount和mount的区别:在文档中描述为"不同的是被存根的子组件",大白话就是shallowMount不会加载子组件,不会被子组件的行为属性影响该组件。
为什么使用shallowMount而不使用mount?
---我认为单元测试的重点在"单元"二字,而不是"测试",想测试子组件再为子组件写对应的测试代码即可
—Wrapper:常见的有一下几种方法
Wrapper:Wrapper 是一个包括了一个挂载组件或 vnode,以及测试该组件或 vnode 的方法。
Wrapper.vm:这是该 Vue 实例。你可以通过 wrapper.vm 访问一个实例所有的方法和属性。
Wrapper.classes: 返回是否拥有该class的dom或者类名数组。
Wrapper.find:返回第一个满足条件的dom。
Wrapper.findAll:返回所有满足条件的dom。
Wrapper.html:返回html字符串。
Wrapper.text:返回内容字符串。
Wrapper.setData:设置该组件的初始data数据。
Wrapper.setProps:设置该组件的初始props数据。 (这是使用了,但没有效果)
Wrapper.trigger:用来触发事件。
<template>
<div class="jest">
<div class="name">{
{
name}}</div>
<div class="name">{
{
name}}{
{
text}}</div>
<div class="text" @click="add">{
{
text}}</div>
</div>
</template>
<script src="./script.js">
export default {
name:"Foo",
props:{
name:{
type: String,
default: '啦啦啦'
}
},
data() {
return {
text: 123
}
},
methods:{
add(){
this.text += 1
}
}
}
</script>
开始测试
import {
shallowMount } from '@vue/test-utils'
import Foo from './Foo.vue'
describe('Foo', () => {
const wrapper = shallowMount(Foo)
console.log(Wrapper.classes()) //['jest']
console.log(Wrapper.classes('jest')) //true
console.log(Wrapper.find('.name').text()) // 切记如果是类的话,要加点 : 啦啦
console.log(Wrapper.findAll('.name')) //返回dom数组 WrapperArray { selector: '.name' }
console.log(Wrapper.findAll('.name').at(0)) //取dom数组中的第一个
Wrapper.setData({
text : 3}) // 设置一个值
console.log(Wrapper.vm.text) // 3
Wrapper.setProps({
name : "拉拉"})
console.log(Wrapper.vm.name) //这个结果仍 为 啦啦啦
Wrapper.find('.text').trigger("click")
console.log(Wrapper.vm.text) // 4
})
也可以初始化某些数据
import {
shallowMount } from '@vue/test-utils'
import Foo from './Foo.vue'
const wrapper = shallowMount(Foo,{
data() {
return {
bar: 'lala'
}
},
propsData:{
message: 'dd'
},
mocks: {
$route: {
query: {
aaa: '1',
}
},
$router: {
push: jest.fn(),
replace: jest.fn(),
}
}
})
5 Jest-Api(使用不同匹配器可以测试输入输出的值是否符合预期)
toBe:判断是否相等
toBeNull:判断是否为null
toBeUndefined:判断是否为undefined
toBeDefined:与上相反
toBeNaN:判断是否为NaN
toBeTruthy:判断是否为true
toBeFalsy:判断是否为false
toContain:数组用,检测是否包含
toHaveLength:数组用,检测数组长度
toEqual:对象用,检测是否相等
toThrow:异常匹配
describe('Foo', () => {
expect(2 + 2).toBe(4)
expect(null).toBeNull()
expect(undefined).toBeUndefined()
let a = 1;
expect(a).toBeDefined()
a = 'ada';
expect(a).toBeNaN()
a = true;
expect(a).toBeTruthy()
a = false;
expect(a).toBeFalsy()
a = [1,2,3];
expect(a).toContain(2)
expect(a).toHaveLength(3)
a = {
a:1};
expect(a).toEqual({
a:1})
})
模拟一个真实使用
import BasicSelect from '@/components/form/BasicSelect.vue';
import {
OptionModel } from '@/components/form/FormBase';
import {
createVue, destroyVM } from '../../../utils'
const options:OptionModel[] = [
{
label: 1, value: 1, disabled: false },
{
label: 2, value: 2, disabled: false },
{
label: 3, value: 3, disabled: false },
{
label: 4, value: 4, disabled: false }
];
const value = ''
describe('BasicSelect', () => {
// 销毁
let select;
afterEach(() => {
destroyVM(select.vm);
});
// 创建select
const getSelect = (params)=>{
return createVue({
template: '<basic-select v-model="value" v-bind="params" />',
data() {
return {
value: value,
params
};
},
components: {
BasicSelect }
});
}
it('select选中后值变更', (done) => {
select = getSelect({
options:options});
expect((select.vm as any).value).toBe(''); // 未选之前 value为空
options.forEach((item:OptionModel,index:number)=>{
(select.vm.$el.querySelector('.el-select') as HTMLElement)?.click();
(select.findAll('.el-select-dropdown__item')).at(index).trigger('click')
expect((select.vm as any).value).toEqual(item.value);
})
done();
})
})
题外话
- 引入了Element ui ,Element 组件会报错,提示没有注册,比如HelloWorld 组件中使用到了el-button组件,就会报错。 解决的话,还是创建一个vue的临时实例,将其挂载上去
- 可以使用createLocalVue
import {
config,createLocalVue } from '@vue/test-utils';
import ElementUI from 'element-ui';
const testVue = createLocalVue();
testVue.use(ElementUI);
export const localVue = testVue;
- 也可以使用stubs解决 但是这个方式 不太合适 是一个数组 里面存放使用的组件
mount(Compo, {
stubs: ['el-button] });
- 在tests文件下下创建setup.js
import Vue from 'vue';
import ElementUI from 'element-ui';
Vue.use(ElementUI);
然后再jest.config.js下 setupFiles: [‘/tests/setup’]
module.exports = {
...
setupFiles: ['<rootDir>/tests/setup']
};
- vue–prop
传入一个对象的所有 property
如果你想要将一个对象的所有 property 都作为 prop 传入,你可以使用不带参数的 v-bind (取代 v-bind:prop-name)。例如,对于一个给定的对象 post:
post: {
id: 1,
title: 'My Journey with Vue'
}
下面的模板:
```typescript
<blog-post v-bind="post"></blog-post>
等价于:
<blog-post
v-bind:id="post.id"
v-bind:title="post.title"
></blog-post>
参考文章
详解Jest结合Vue-test-utils使用的初步实践
关于Vue中用jest测试
Jest 从入门到入坟
一文搞定前端自动化测试(基础篇)
jest官方文档
第一次使用jest 欢迎大家指导~
边栏推荐
- 请问为什么sqlserver cdc,任务启动过了一天,会报这个错误,明明已经开启cdc了。
- ATH10传感器读取温湿度
- 调试ZYNQ的u-boot 2017.3 不能正常启动,记录调试过程
- Regular backup of mysql database (retain backups for nearly 7 days)
- Big guy, when Oracle single-table incremental synchronization, the source database server takes up nearly 2g of memory. This is not normal, right?
- PLSQL学习第三天
- COLMAP+OpenMVS实现物体三维重建mesh模型
- 基于STC8G2K64S4单片机通过OLED屏幕显示模拟量光敏模拟值
- 复杂AB实验
- 【机器学习】神经网络中的优化器
猜你喜欢
随机推荐
2022 Henan Mengxin League Game (5): University of Information Engineering F - Split Turf
关于数据库中的中文模糊检索探讨
2022 Henan Mengxin League No. 5: University of Information Engineering B - Transportation Renovation
ESP32 485风速
【机器学习】神经网络中的优化器
数据库公共字段自动填充
Nude speech - lying flat - brushing questions - big factory (several tips for Android interviews)
排序二叉树代码
I would like to ask you guys, when FLink SQL reads the source, specify the time field of the watermark. If the specified field is in the grid
【MySQL】SQL语句
3.事务篇【mysql高级】
DGIOT支持工业设备租赁以及远程管控
1413. 逐步求和得到正数的最小值
If the data of the oracle business table is added, deleted, or modified, will the index of the table write redo and undo?
Complex AB experiment
软件测试面试题避雷(HR面试题)最常见的面试问题和技巧性答复
幂函数 指数函数 对数函数
2022 Henan Mengxin League Game (5): University of Information Engineering C - Throwing a Handkerchief
【Event Preview on August 9】Prometheus Summit
High quality WordPress download station 5 play theme template









