本次我会与你们分享一下我学测试时候记的笔记知识以及本次项目里面作的几个测试.
前端代码的单元测试与集成测试属于雷声大雨点小, 不少人一提到它都说是个好东西, 试问又有几个公司的vue项目是严格要求跑单元测试与集成测试的那?? 测试没经过是否暂停上线? 除了大公司没有几家作获得吧, 毕竟大多数公司只是让专业的测试团队进行'人肉测试'.
如今前端体系搞得好庞大, 围绕着前端开发的技术与知识点层出不穷, 更别说各类技术之间那剪不断理还乱的纠葛, 我听有人说过: "我只想好好写前端代码, 其余的无论行不行", 这句话是个病句, 这些杂七杂八的技术也都是前端技术, 若是你只会写你所谓的'前端代码', 那你真的只能是一生'初学者'了┑( ̄Д  ̄)┍.
对于这我的人都说好, 可是人人不咋用是咋回事那??🙅♂️接下来咱们就他的优缺点进行罗列.前端
缺点vue
优势node
大致上分为两类:git
基本搭建
我是在vue项目里面直接选择的jest测试
单独实验的朋友能够自行安装 npm i jest -D
去配置一下github
"scripts": { "test": "jest --watchAll" },
命令行里运行npm run test便可
若是电脑运行说没有这个命令的话, 能够用npx 或者在全局安装一下
若是是es项目的话, 要集成一下 npm i @babel/core @babel/preset-env -D
jest内置了 对babel的依赖, 他看到.babelrc就会去配合解析的面试
基本使用
jest 会自动查找 xx.test.js的文件, 配置以下
随便修改为你喜欢的语义化就好, element-ui采用的是spec
这面这个文件能够经过, jest init生成
jest.config.jsvue-cli
testMatch: [ '**/tests/unit/**/*.(spec|test).(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)' ],
// 1: 最外层describe至关于一个大的父容器盒子, 把测试进行分'块' // 在出错的时候, 控制台会报出是哪一'块'出错了 describe('按钮相关代码', () => { // 2: '小块'测试单元, 具体的某些职责的测试, test('测试 按钮点击小伙', () => { // 3: 断言, 也就是真正判断某些值是否正确的一步 expect(1).toBe(1); }); });
以偶上述为例npm
// 意思就是判断, 1 是否 === 1 expect(1).toBe(1); // 由此可知, expect函数负责接收要测试的值 // toBe则为 所谓的 ===, 与他里面的值进行比较 // 那既然有 === 确定就会有更多种类型的判断了 // 他学名叫配置器
多种类型的'配置器'element-ui
生命周期设计模式
beforeEach(() => { // 每一个test执行以前都会执行我 }); afterEach(() => { // 每一个test执行以后都会执行我 }); beforeAll(() => { // 全部test执行以前执行我 }); afterAll(() => { // 全部test都执行完执行我 }); describe('按钮相关代码', () => { test('测试 按钮点击小伙', () => { expect(1).toBe(1); }); });
这个时代全部插件的配置都趋于'函数化'
上面的生命周期函数很符合设计模式, 咱们在写项目的时候也能够借鉴一下.
看完上面这些是否是感受测试页很容易, 坑的在后面结合vue项目时.
vue里面固然天差地别, 渲染方式都不同了, 这个还好有vue本身团队提供的支持
介绍几个vue里面的概念
因为篇幅有限, 我就直接拿我工程里面的举例子了;
其实到底要测些什么这方面, 我理解的也不是很透, 因此只是简单的几个例子, 一块儿学习一块儿讨论.
按钮组件
按钮的测试
vue-cc-ui/tests/unit/Button.test.js
// shallowMount是@vue/test-utils官方提供的测试工具 import { shallowMount } from '@vue/test-utils'; import Button from '../../src/components/Button'; // 这是参考网上封装的获取dom的方法, 下面会有说明👇 import { findTestWrapper } from '../utils/util'; describe('测试button组件', () => { it('1: 能够渲染出button组件', () => { // 利用shallowMount实例化个人button组件 const wrapper = shallowMount(Button); // 关键词contains, 判断 Wrapper 是否包含了一个匹配选择器的元素或组件。 // 也就是我想判断, 这个button组件渲染完毕, 页面上是否真的有一个button元素 expect(wrapper.contains('button')).toBe(true); }); it('2: button组件点击时会触发click事件', () => { // 依旧是先渲染 const wrapper = shallowMount(Button); // 找到button实例, 这里的at(0), 相似数组的[0]; const button = findTestWrapper(wrapper,'button').at(0); // 在button身上触发其click方法 button.trigger('click'); // emitted : 返回一个包含由 Wrapper vm 触发的自定义事件的对象。 // 也就是监听是否页面里面出发了 this.$emit('click')事件 // toBeTruthy 这个咱们👆上面讲过了 expect(wrapper.emitted().click).toBeTruthy(); }); it('3: 传入icon参数, 能够显示icon组件', () => { // shallowMount初始化时, 能够传递参数进去 // 下面的操做你们都懂 const wrapper = shallowMount(Button,{ propsData:{ icon:'cc-up' } }); // 找到和这个icon元素 const icon = findTestWrapper(wrapper,'icon').at(0); // 在我传递了icon以后, 这个icon组件必须存在 expect(icon).toBeTruthy(); }); });
上面的例子里面提到了一个公共方法我来解释一下
export const findTestWrapper = (wrapper, tag) => { return wrapper.findAll(`[data-test="${tag}"]`); };
咱们在书写代码的时候, 为了方便之后的测试, 也会添加一些测试属性, 好比下面这种
<div data-test='name'> {{name}} </div>
取值:
findTestWrapper(wrapper,'name')
findAll 是 wrapper身上的方法, 与之对应还有find 只找寻一个
输入框的测试
import { shallowMount } from '@vue/test-utils'; import Input from '../../src/components/Input'; import { findTestWrapper } from '../utils/util'; describe('测试button组件', () => { it('1: 能够渲染出Input组件', () => { // 这个属于基础步骤了 const wrapper = shallowMount(Input); expect(wrapper.contains('input')).toBe(true); }); it('2: 输入value与显示的内容相同, 而且修改联动', () => { // 测试是否双向绑定 const wrapper = shallowMount(Input,{ propsData:{ value:'内容1' } }); // 取到输入框实例 const input = findTestWrapper(wrapper,'input').at(0); // element就是直接取到dom了...这个dom也是未dom // value能够模拟的拿出显示的值 expect(input.element.value).toBe('内容1') // 改变也随之改变 wrapper.setProps({ value: '内容2' }) // 只要一块儿变了就知足需求 expect(input.element.value).toBe('内容2') }); // 个人输入框是有清除功能的额 it('3: 清除内容按钮有效', () => { const wrapper = shallowMount(Input,{ propsData:{ value:'内容1', clear:true } }); // hover 时候才会出现!! // 这是组件的内部触发条件, setData能够强行改变组件内部的data数据 wrapper.setData({ hovering:true }) const clear = findTestWrapper(wrapper,'clear').at(0); // 这里也讲过toBeTruthy能够判断是否可转true // 也就是这个定义的实例是否存在 expect(clear).toBeTruthy(); // 触发清除事件 clear.trigger('click'); expect(wrapper.emitted().input).toBeTruthy(); }); it('4: 传入icon参数, 能够显示icon组件', () => { const wrapper = shallowMount(Input,{ propsData:{ icon:'cc-up' } }); const icon = findTestWrapper(wrapper,'icon').at(0); expect(icon).toBeTruthy(); }); it('5: 切换type, 出现文本框', () => { const wrapper = shallowMount(Input,{ propsData:{ type:'textarea' } }); const textarea = findTestWrapper(wrapper,'textarea').at(0); expect(textarea).toBeTruthy(); }); });
测试分页器
import { shallowMount } from '@vue/test-utils'; import Pagination from '../../src/components/Pagination'; import { findTestWrapper } from '../utils/util'; describe('测试分页器组件', () => { it('1: 能够渲染出分页器组件', () => { const wrapper = shallowMount(Pagination,{ propsData:{ pageTotal:5, value:1 } }); // classes 返回 Wrapper DOM 节点的 class。返回 class 名称的数组。或在提供 class 名的时候返回一个布尔值。这个的意思就是 这个dom的class 是 'cc-pagination' expect(wrapper.classes()).toContain('cc-pagination'); }); it('2: 传入1000页是否显示1000页', () => { const wrapper = shallowMount(Pagination, { propsData:{ pageTotal:1000, pageSize:1000, value:1 } }); const li = findTestWrapper(wrapper, 'item'); // 这个元素我获取到了1000个 expect(li.length).toBe(1000); }); it('3: 点击第三页是否跳转到第三页', () => { const wrapper = shallowMount(Pagination, { propsData:{ pageTotal:10, pageSize:10, value:1 } }); wrapper.vm.handlClick(3) // 发送事件 expect(wrapper.emitted().input).toBeTruthy(); // 发送事件的参数, 注意,是数组的形式 // 这个事件发送的第一个参数[0] expect(wrapper.emitted().input[0]).toEqual([3]) }); });
写到这里你们对测试也应该有了不少本身的想法, 没试过的小伙伴不妨试一试.
配置
上面没有提: 开启实时检测
"test:unit": "vue-cli-service test:unit --watch",
// 无论改没改, 全部文件都监控 "test:unit": "vue-cli-service test:unit --watchAll",
end
一套ui组件不写测试也是说不过去的, 写的过程也遇到不少不少的坑, 好比说两个相互以插槽嵌套的组件, 两个又都有'必传参数'的限制, vue没有很好的解决这个问题, 文档看了很久, 跟个人感受就是有用的东西太少, 没办法这就是现状, 但愿测试相关技术支持愈来愈完善吧.
你们均可以一块儿交流, 共同窗习,共同进步, 早日实现自我价值!!