技术选型
- 测试框架及断言库 Jest
在第一篇整体介绍中,项目初期是使用mocha
+chai
组合去做单元测试的框架的,由于项目整体所使用的Typescript
来编写,发现在这套选型中对于coverage
(测试覆盖率)的输出会有一定的影响,无法检测ts后缀的文件,并且在tsconfig
中配置了map设置后,在生成测试覆盖率的目录中会出现重复输出的bug,暂时没有很好的解决方案,所以在升级了脚手架和TS版本后,我们采用Jest
框架来做基础。 - Vue测试框架 Vue-test-utils
在单元测试中我们无法避免的需要对DOM进行抓取,或者对组件进行挂载,触发DOM的事件等。原生的querySelector
以及event
会显得非常的麻烦。
Vue-test-utils是Vue的官方的单元测试框架,它提供了一系列非常方便的工具,使我们更加轻松的为Vue构建的应用来编写单元测试。主流的 JavaScript 测试运行器有很多,但 Vue Test Utils 都能够支持。它是测试运行器无关的。
我认为可以这样理解,Vue-test-utils在Vue和Jest之前提供了一个桥梁,暴露出一些接口,让我们更加方便的通过Jest为Vue应用编写单元测试。 - 测试覆盖率 Istanbul
在测试中总是免不了测试覆盖率报告连量化我们的测试用例所覆盖的代码范围,当然这只是一个参考性的作用,但绝不是可以忽视的一环。我们可以通过这个报告客观地看我们哪些代码被覆盖被运行而哪些没有,也可以通过标注看代码被调用地次数来判断是否符合预期。
项目配置
目录结构
|-- __mock__ // 项目mock数据
|-- conf // 项目配置
|-- src // Source directory
| |-- App.vue // Main component
| |-- main.ts // Entry file
| |...
|-- public // 输出模板文件
|-- tests // 测试目录
| |-- __mocks__ // 单元测试模拟数据
| | |-- axios.ts // ajax mock方法
| | |-- xxx.ts // 模拟数据
| |-- coverage // 测试覆盖率报告
| |-- e2e // E2E测试
| | |-- config
| | |-- fixtures
| | |-- plugins
| | |-- specs
| | |-- support
| |-- unit // 单元测试
| | |-- xxx.spec.ts // 测试用例
|-- bable.config.js // ES6 syntax compiler configuration
|-- .gitignore // Ignored file
|-- cypress.json // Cypress指定入口文件及项目ID
|-- jest.config.js // Jest配置文件
|-- package.json // Dependent configuration file
|-- README.md // Readme
|-- tsconfig.json // Typescript配置文件
|-- tslint.json // Tslint语法配置文件
|-- vue.config.js // Webpack配置
配置介绍
在项目创建后会生成一个单独的jest.config.js,这个配置文件需要稍微更改一下才能运行测试用例。
module.exports = {
moduleFileExtensions: [
'js',
'jsx',
'json',
'vue',
'ts',
'tsx'
],
transform: {
'^.+\\.vue$': 'vue-jest',
'.+\\.(css|styl|less|sass|scss|png|jpg|ttf|woff|woff2)$': 'jest-transform-stub',
'^.+\\.tsx?$': 'ts-jest',
'^.+\\.js$': 'babel-jest'
},
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1'
},
snapshotSerializers: [
'jest-serializer-vue'
],
testMatch: [
'**/tests/unit/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)'
],
transformIgnorePatterns: ['<rootDir>/node_modules/'],
testURL: 'http://localhost:8080/',
automock: false,
collectCoverage: true,
coveragePathIgnorePatterns: [".*\\.d\\.ts", "<rootDir>/node_modules/"],
collectCoverageFrom: [
"src/**/*.{js,vue,ts}",
"!**/node_modules/**",
"!**/tests/**",
"!src/libs/**",
"!src/ssh.js",
"!**/dist/**"
],
coverageReporters: ["html", "text-summary"],
coverageDirectory: '<rootDir>/tests/coverage'
}
我们在这里需要单独更改的配置项为testURL
,测试用例中组件被挂载后,如果有请求API的操作,在没有配置这个选项时会抛出一个找不到127.0.0.0:80的错误,需要配置项目被启动时所依赖的地址以及路径,以便请求从这个路由被发出,我这里选用的为http://localhost:8080/
,这个参数最好配置一个动态参数,判断是否是开发环境。
还需要配置的是collectCoverage
,coveragePathIgnorePatterns
,collectCoverageFrom
,coverageReporters
,coverageDirectory
,这些配置项是为了测试覆盖报告,依次为是否收集,include和exclude的文件类型和目录,收集报告后展现的文件形式以及生成报告的目录存放位置和名称。
单元测试架构
除了存放单元测试的文件夹unit外,我们在许多业务场景下需要对API进行mock,所以需要有一个地方专门存放我们的mock数据以及函数,在Jest
中,这方面的资源必须被存放在__mocks__
这个目录下面才会生效。所有的测试用例存放在unit
下面,文件的命名为xxx.spec.ts
或spec.js
后缀,前缀为了区分测试集合所针对的组件,统一命名为组件的名称,如果有该组件的子组件则向后使用驼峰法叠加即可。
基础语法结构
import Vue from 'vue'
import { shallowMount } from '@vue/test-utils';
import StartQLink from '@/views/Steps/views/StartQLink/StartQLink.vue'
import mockAxios from '../__mocks__/axios'
import { qlinks } from '../__mocks__/startQlink'
describe('StartQLink.vue', () => {
it('获取QLink', () => {
const wrapper = shallowMount(StartQLink)
const vm: any = wrapper.vm
mockAxios.get.mockImplementationOnce(() => {
return Promise.resolve({
data: {
data: qlinks,
error_code: 0,
message: '',
},
})
})
vm.getQlinks()
Vue.nextTick(() => {
Vue.nextTick(() => {
expect(vm.qlinks).toEqual(qlinks)
})
})
})
})
在这个测试集合案例中,我们需要引入Vue
实例,需要的测试框架vue-test-utils
,需要的测试的组件StartQLink.vue
,最后引入的是我们所mock掉的axios
的请求函数以及我们的假数据,这方面我会在后面的专题中讲解。
首先,一个测试集合的表达式为describe
,后面传入的第一个参数为一个字符串,通常为测试组件的名称,一个describe
中可以大于等于一个it
的测试用例,这个测试用例的数量不可以为零,否则会报错。在测试用例中我们首先需要使用shallowMount
去挂载我们的组件,然后通过访问里面的vm
去访问这个组件的实例。这样我们就可以得到这个组件所有的data
,function
等属性。这样我们就组成了一套基本的测试集合,这里建议每个测试集合的开头都需要挂载一次组件,我刚开始把挂载的方法都写在了describe
中,每个测试用例可能会对该实例有一个更改的操作,这样下面的用例在访问该实例的时候得到的极有可能不是初始状态的组件,所以需要在每个it
的开头重新挂载一次。
在这个测试用例中最终我们通过一个断言expect
来确认结果是否符合预期,这也是每个测试用例最终的目的,在经过一系列的操作、触发事件甚至是异步请求后,最后需要去判断DOM或是组件本身的状态是否符合要求,这就是每个测试用例的职责所在。
总结
这一章主要讲解项目的整体目录结构,单元测试所在项目中的放置位置以及目录的分布情况,还讲解了一个测试用例的命名规范,大体的结构是什么,下一章会根据Jest
以及vue-test-utils
的API进行语法介绍和演示。