单元测试
在新建项目的时候就将“单元测试”这个选项选上,CLI脚手架会自动帮助生成单元测试文件,依赖库都会帮助安装好。
a)jest和mocha。js是由facebook开发的,不需要自己去写js DOM以及断言,已经内置了断言库。
b)@vue/test-utils
c) sinon:创建测试的替身
先自己想想测试该怎么做。先将这个测试单元取个名字,说明是要测试哪个单元的程序。然后分析对这个程序要进行哪些角度的测试。如果是一个框,则可以从视图和计算结果两个方面来测试是否正确,视图可以测试与快照是否相同,不同的话就不通过。计算结果的话就要先写出输入内容和输出结果,如果网页上的输出结果跟测试单元里面的输出结果是一样的就是对的。
以下这个Vue实例,功能有:每点一次按钮数字就要加1,同时还要将count当前的值(this.count)发射到“change”这个频道中,由它的父组件通过侦听来接收。
Counter.vue
单元测试文件不是vue实例,是js文件
import { mount } from "@vue/test-utils"; //mount我的理解是锚定,就是复制(镜像)需要测试的Vue实例,类似DPI将流量镜像一遍进行检查。
import Counter from "@/components/Counter.vue"; //将单元测试的对象import到单元测试的vue中,以便后续锚定。要测的就是Counter.vue
import sinon from "sinon"; //一个辅助测试的插件
describe("Counter.vue", () => { //给本单元测试取个名字
const change = sinon.spy(); //这个sinon.spy()函数就是安插在wrapper(Counter的复制品)中的一个间谍函数,负责拦截实例发出去的信息。所以如果sinon.spy()函数获取了值,而且这个值==this.count的话,就说明Count这个实例的emit功能没有问题。
const wrapper = mount(Counter, { //锚定Counter这个vue实例
listeners: {
change //wrapper要监听间谍change的动态。
}
});
it("renders counter html", () => { //it表示一个单元测试,它的名字叫做"renders counter html",wrapper就是Counter的复制品,这个复制品的html展现形式将与Counter本身的快照进行对比,如果一样就表示测试通过。对比快照用命令toMatchSnapshot()。
expect(wrapper.html()).toMatchSnapshot();
});
it("count++", () => { //it表示另外一个针对Counter的单元测试,名字叫做“count++”。
const button = wrapper.find("button"); //在wrapper(即Counter的复制品)中找到button这个按钮
button.trigger("click"); //然后通过click动作触发button这个按钮
expect(wrapper.vm.count).toBe(1); //点击按钮后希望数值为1
expect(change.called).toBe(true); //点击按钮后希望间谍被触发(change.called=true)
button.trigger("click"); //再一次点击按钮
expect(change.callCount).toBe(2); //希望间谍被触发的次数为2
});
});
以上是从唐金州的视频中的例子。
-----------------------------------------------------------------------------
以下是从“2小时学会vue单元测试”中学习到的例子。安装的不是jest,是mocha,断言库用的是chai
https://www.bilibili.com/video/BV18W411j725?p=5&spm_id_from=pageDriver
TestDemo.vue 这是从vue的官网的vue单元测试的例子
{{ error }}
TestDemo.spec.js的配置文件
import Vue from "vue"
import { expect } from "chai"
import Hello from "@/components/TestDemo.vue"
describe("Hello", () => {
it("notice informaiton should be showd correctly",()=>{
// 正常的主程序是使用html来渲染username得到一个页面,但是这里既然是做测试,就是模拟html来渲染,使用的是Vue.extends来渲染的。
// 所以第一步是将Hello这个组件用Vue.extends来渲染。
const renderbynode=Vue.extends(Hello)
// 渲染完了按照道理就可以在渲染的结果中寻找自己检查的东西了,但是为啥这里还要有一个mount,不明白
const wrapper = new renderbynode().$mount()
//测试例1:给wrapper的输入框中输入7个空格,所期待的结果是在页面中有一个error字段。
// `username` 在除去头尾空格之后不应该少于 7 个字符
wrapper.setData({ username: ' '.repeat(7) })
// 确认错误信息被渲染了,有错误信息,结果是true
expect(wrapper.find('.error').exists()).to.not.be(false)
//测试例2:给wrapper的输入框中输入'Lachlan' ,所期待的结果是在页面中没有error字段。
//如果有error字段,则是错误的,没有通过。
// 将名字更新至足够长
wrapper.setData({ username: 'Lachlan' })
// 断言错误信息不再显示了
expect(wrapper.find('.error').exists()).to.not.be(true)
})
})
问题1:xxx is not a function的问题。完全按照vue单元测试的官网写就没有问题。
问题2:it中定义的xxx.username=“123”,无效。也就是说无法更改页面中username的初始设置。
需要加入一个factory function,用于模拟数据的输入。
import { expect } from "chai" import { shallowMount } from '@vue/test-utils' import Hello from "@/components/TestDemo.vue" const factory = (values = {}) => { return shallowMount(Hello, { data () { return { ...values } } }) } describe("Hello", () => { it("1-h1 title",()=>{ const vm = factory() expect(vm.find('.hello').text()).equal("hello world") }), describe("2-input", () => { it("2.1-input longer than 7",()=>{ const vm = factory({username:"12345678"}) expect(vm.find('.error').text()).equal("Correct enter") }), it("2.2-input shorter than 7",()=>{ const vm = factory({username:"123"}) expect(vm.find('.error').text()).equal('Please enter a longer username') }) }) })