----------------- 11-5更新 -------------------
哎呀,今天太忙了,被实现大数据表格搞得头晕脑涨,就当写日记了吧,立个Flag
----------------- 11-6更新 -------------------
这篇文章主要介绍如何对Vue组件进行单元测试。我们首先介绍一下单元测试对于开发一个可维护性好的应用的重要性和我们该测试什么。
详细内容包括以下部分:
- 为一个Vue组件新建并运行一个单元测试
- 从各个角度测试一个Vue组件
- 使用mock模拟数据测试异步函数
- 检测单元测试的覆盖率
- 创建单元测试文件
环境准备
- Vue - JavaScript框架
- Vue CLI - Vue开发工具
- Jest - JavaScript测试框架
- Node - JavaScript运行环境
学习目标
- 能够解释单元测试的重要性
- 描述需要测试(或者不应该测试)的内容
- 为一个Vue组件写一个测试套件(unit test suite)
- 使用Vue CLI为你的Vue项目运行单元测试
- 在测试套件中使用beforeEach()和afterEach()方法
- 为Vue组件中的实现细节编写单元测试
- 为Vue组件的行为编写单元测试(点击事件等等)
- 理解为什么mock数据有助于单元测试
- 为mock库和异步函数编写单元测试
- 检测单元测试覆盖率
- 为Vue组件编写一个具有良好架构的单元测试文件
为什么需要单元测试
一般情况下,测试是为了更好地保证你的应用在你的用户面前可以符合预期的那样运行。
一个软件即便拥有很高的测试覆盖率也绝对无法证明是完美的,但可以作为判断一个软件质量的最初指标。另外,可测试的代码一般可以证明一个软件具有良好的架构,这就是为什么一个高级的工程师在整个开发中都会考虑测试。
测试可以分成三个阶段:
- 单元(unit)
- 整体(integration)
- 用户(end-to-end)
单元测试就是将部分独立的具有某些功能代码从整个项目中分离出来并对其进行测试,这个是防止错误bug和逻辑矛盾的第一个措施,单元测试是作为测试驱动开发(TDD)过程中一部分,啥是测试驱动开发?顾名思义,自己百度!
单元测试可以提高代码的可维护性
可维护性有助于加强你代码的健壮性或者说有助于bug修复,也有助未来其他开发者对你的代码进行重构升级
单元测试应该与持续集成(CI)流程相结合,以确保单元测试能够持续执行,理想状态下,是在每次提交到你的仓库时执行,一个稳定的测试套件可以保证在你开发过程中及时的捕捉到异常并修复,避免你的用户在生产环境碰到他。
----------------- 11-7更新 -------------------
测试什么
你应该测试什么,或者说你不应该测试什么。
就单元测试而言,有三种东西可以测试:
- 实现细节,一个组件根据输入得到某种结果的底层业务逻辑
- 公共方法/组件,根据特定输入的到特定的结果
- 附带影响,比如点击一个按钮触发了某个事件
因为你不可能测试所有东西,所以我们的关注点应该在哪里?关注用户交互位置的输入输出,用户的直接体验永远在第一位。通过关注某个模块的输入输出,让你只测试用户会要体验到东西。
- 输入:data, props, user interaction, lifecycle methods, Vuex store, route params, query strings
- 输出:rendered output, events, data results, Vuex store updates, dispatches
或许还有很多内部复杂的逻辑需要测试,但这些测试最好是在过后重构优化代码的过程中完成。
----------------- 11-8更新 -------------------
在Vue中进行单元测试
因为组件就像是vue中的一块块积木,也是你整个应用中重要的组成部分。所以花时间去为你的组件编写单元测试吧。
一个好的测试工具满足以下几点:
- 易于编写测试
- 快速编写测试
- 通过一个简单的命令行运行测试
- 测试运行够快
我希望你能感受单元测试的乐趣,并驱使你做更多的测试
单元测试工具
- Vue Test Utils - Vue官方的单元测试工具包
- Jest - 负责找到测试文件并执行测试的运行工具
单元测试概述
开始之前,让我们先谈谈关于在vue中单元测试文件的命名规定,单元测试文件应该按照以下格式命名:
组件名.spec.js
通常情况下,需要为每个vue组件准备一个单元测试文件,每个文件中包含一个测试套件或者多个测试套件
所有测试文件需要跟vue组件的存放位置要分开,像放在tests/unit文件夹下:
├── node_modules
├── public
├── src
│ ├── assets
│ └── components
└── tests
└── unit
运行测试
Vue cli可以通过执行以下命令使用jest来运行单元测试:
npm run test:unit
得到下面结果
> vue-weather-app@0.1.0 test:unit vue-weather-app
> vue-cli-service test:unit
PASS tests/unit/Footer.spec.js
PASS tests/unit/Header.spec.js
PASS tests/unit/Banner.spec.js
PASS tests/unit/Weather.spec.js
PASS tests/unit/Search.spec.js
PASS tests/unit/App.spec.js
=============================== Coverage summary ===============================
Statements : 89.36% ( 42/47 )
Branches : 90% ( 9/10 )
Functions : 94.12% ( 16/17 )
Lines : 89.36% ( 42/47 )
================================================================================
Test Suites: 6 passed, 6 total
Tests: 22 passed, 22 total
Snapshots: 0 total
Time: 4.255s
Ran all test suites.
示例
使用Vue Weather App这个看天气的小项目来看一些测试的例子
示例1
让我们来看看vue单元测试的第一个示例,第一个单元测试文件在tests/unit/Header.spec.js下用来测试header组件
import {
shallowMount } from '@vue/test-utils'
import Header from '@/components/Header.vue'
describe('Header.vue Test', () => {
it('renders message when component is created', () => {
// render the component
const wrapper = shallowMount(Header, {
propsData: {
title: 'Vue Project'
}
})
// check the name of the component
expect(wrapper.name()).toMatch('Header')
// check that the title is rendered
expect(wrapper.text()).toMatch('Vue Project')
})
})
mounting
在Header.spec.js测试文件中的第一行从Vue Test Utils库中引入了一个叫shallowMount的方法,所谓mounting意思就是每个独立组件的加载到可以测试的状态
Vue Test Utils中有两个方法:
- shallowMount() -创建一个wrapper包裹住Vue组件,但不包含子组件,只保留子组件占有的位置(推荐)
- mount() - 跟shallowMount()一样但包含子组件
因为我们要测试的是每个独立的组件,所以我们接下来使用的是shallowMount()
**shallowMount()**对测试每个独立的组件来说是更好的选择,因为子组件已经被排除,是单元测试的理想状态
另外, **shallowMount()**对测试的执行速度有很大提升,因为没有了渲染子组件的性能消耗
**mount()**在你想要测试子组件行为的时候同样也有用
在代码的第二行引入了要测试的vue组件,Header.vue
Describe
在使用了import声明之后,describe定义了一个测试套件(unit test suite)
在单元测试文件中,可以拥有多个定义了不同unit test suite的describe,意味着每个describe可以使用it包含多个单元测试
describe和it其中的区别就是:
- describe - unit test suite
- it - 每个独立的单元测试方法
在vue中进行单元测试很棒的地方就是它拥有很多内置的方法添加注释,比如,describe允许你在第一个参数传入该unit test suite的命名,可以传入要测试的组件名字方便看哪个组件的测试结果
it同样地可以传入每个测试的描述,比如测试组件被创建时的渲染信息。
Expects
在实际单元测试中,第一步就是加载Vue组件然后才能被测试
// 渲染组件
const wrapper = shallowMount(Header, {
propsData: {
title: 'Vue Project'
}
})
shallowMount方法返回一个包含了已经加载的component和测试该组件方法的wrapper对象,wrapper对象允许我们测试所有Vue component生成的HTML和属性(比如data)
另外,传递给Header组件的props属性,要放在shallowMount方法的第二个参数上
真正运行测试的代码:
// 检查测试组件的名字
expect(wrapper.name()).toMatch('Header')
// 检查标题是否被渲染
expect(wrapper.text()).toMatch('Vue Project')
这两行代码在wrapper上执行以下检查来测试Vue组件,vue官方文档有说明wrapper可以检查什么
- 检查组件的名字是不是Header
- 检查组件生成的标题是不是’Vue Project’
这些检查都是比较string字符串,所以推荐的测试方法是toMatch()
Jest Helpers
因为Header组件只检查了string值,但Jest其实还有很多可选的测试类型可以检查,详细可查看Jest官方文档(或者等我之后补充)。
另外,还有not限定符可以使用
expect(wrapper.name()).not.toMatch('Header')
示例2 - 测试初始条件
该示例会展示如何提前设置好Vue组件的初始条件(或者说状态)。
tests/unit/Weather.spec.js
import {
shallowMount } from '@vue/test-utils'
import Weather from '@/components/Weather.vue'
describe('Weather.vue Implementation Test', () => {
let wrapper = null
// 运行单元测试前的配置
beforeEach(() => {
// render the component
wrapper = shallowMount(Weather, {
propsData: {
city: '',
weatherSummary: '',
weatherDescription: '',
currentTemperature: 0.0,
lowTemperature: 0.0