jest 底层原理代码
function add(a, b) {
return a + b
}
function mutil(c, d) {
return c * d
}
function expect(result) {
return {
toBe: function (actual) {
if (result !== actual) {
throw new Error('预期值与实际值不符!')
}
}
}
}
function test(desc, fn) {
try {
fn()
console.log(`测试结果${desc},测试通过`)
} catch (e) {
console.log(`测试结果 ${desc} 不通过 ${e}`)
}
}
test('3 + 4 等于 7', () => {
expect(add(3, 4)).toBe(7)
}) // 测试结果3 + 4 等于 7,测试通过
test('3 * 4 等于 12', () => {
expect(mutil(3, 4)).toBe(14)
}) // 测试结果 3 * 4 等于 12 不通过 Error: 预期值与实际值不符!
测试框架 Jest
优点
速度快、API简单、易配置、隔离性好、监控模式、IDE整合、Snapshot(快照)、多项目并行、覆盖率、Mock丰富等。
jest 分为单元测试,也就是模块测试;集成测试,是多个模块测试。
Jest 使用方式 CommonJs
1、首先安装jest,例如:npm install jest@24.8.0 -D
2、main.js 文件代码:
function add(a, b) {
return a + b
}
function mutil(c, d) {
return c * d
}
// 使用 commonjs 的方式导出模块
module.exports = {
add,
mutil
}
3、main.test.js 文件代码:
const {add, mutil} = main
test('3 + 4 等于 7', () => {
expect(add(3, 4)).toBe(7)
})
test('3 * 4 等于 12', () => {
expect(mutil(3, 4)).toBe(12)
})
4、在 package.json文件中配置运行的测试命令
"scripts": { "test": "jest" },
5、命令行运行 npm run test
Jest 的简单配置
运行命令 npx jest --init, 自动生成 jest.config.js
jest.config.js
// https://jestjs.io/docs/en/configuration.html
module.exports = {
// All imported modules in your tests should be mocked automatically
// automock: false,
// Stop running tests after `n` failures
// bail: 0,
// Respect "browser" field in package.json when resolving modules
// browser: false,
// The directory where Jest should store its cached dependency information
// cacheDirectory: "/private/var/folders/ct/797qh2lx05v_hgtjn068rmq80000gn/T/jest_dx",
// Automatically clear mock calls and instances between every test
clearMocks: true,
// Indicates whether the coverage information should be collected while executing the test
// collectCoverage: false,
// An array of glob patterns indicating a set of files for which coverage information should be collected
// collectCoverageFrom: null,
// The directory where Jest should output its coverage files
coverageDirectory: "coverage",
// An array of regexp pattern strings used to skip coverage collection
// coveragePathIgnorePatterns: [
// "/node_modules/"
// ],
// A list of reporter names that Jest uses when writing coverage reports
// coverageReporters: [
// "json",
// "text",
// "lcov",
// "clover"
// ],
// An object that configures minimum threshold enforcement for coverage results
// coverageThreshold: null,
// A path to a custom dependency extractor
// dependencyExtractor: null,
// Make calling deprecated APIs throw helpful error messages
// errorOnDeprecated: false,
// Force coverage collection from ignored files using an array of glob patterns
// forceCoverageMatch: [],
// A path to a module which exports an async function that is triggered once before all test suites
// globalSetup: null,
// A path to a module which exports an async function that is triggered once after all test suites
// globalTeardown: null,
// A set of global variables that need to be available in all test environments
// globals: {},
// The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers.
// maxWorkers: "50%",
// An array of directory names to be searched recursively up from the requiring module's location
// moduleDirectories: [
// "node_modules"
// ],
// An array of file extensions your modules use
// moduleFileExtensions: [
// "js",
// "json",
// "jsx",
// "ts",
// "tsx",
// "node"
// ],
// A map from regular expressions to module names that allow to stub out resources with a single module
// moduleNameMapper: {},
// An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader
// modulePathIgnorePatterns: [],
// Activates notifications for test results
// notify: false,
// An enum that specifies notification mode. Requires { notify: true }
// notifyMode: "failure-change",
// A preset that is used as a base for Jest's configuration
// preset: null,
// Run tests from one or more projects
// projects: null,
// Use this configuration option to add custom reporters to Jest
// reporters: undefined,
// Automatically reset mock state between every test
// resetMocks: false,
// Reset the module registry before running each individual test
// resetModules: false,
// A path to a custom resolver
// resolver: null,
// Automatically restore mock state between every test
// restoreMocks: false,
// The root directory that Jest should scan for tests and modules within
// rootDir: null,
// A list of paths to directories that Jest should use to search for files in
// roots: [
// "<rootDir>"
// ],
// Allows you to use a custom runner instead of Jest's default test runner
// runner: "jest-runner",
// The paths to modules that run some code to configure or set up the testing environment before each test
// setupFiles: [],
// A list of paths to modules that run some code to configure or set up the testing framework before each test
// setupFilesAfterEnv: [],
// A list of paths to snapshot serializer modules Jest should use for snapshot testing
// snapshotSerializers: [],
// The test environment that will be used for testing
// testEnvironment: "jest-environment-jsdom",
// Options that will be passed to the testEnvironment
// testEnvironmentOptions: {},
// Adds a location field to test results
// testLocationInResults: false,
// The glob patterns Jest uses to detect test files
// testMatch: [
// "**/__tests__/**/*.[jt]s?(x)",
// "**/?(*.)+(spec|test).[tj]s?(x)"
// ],
// An array of regexp pattern strings that are matched against all test paths, matched tests are skipped
// testPathIgnorePatterns: [
// "/node_modules/"
// ],
// The regexp pattern or array of patterns that Jest uses to detect test files
// testRegex: [],
// This option allows the use of a custom results processor
// testResultsProcessor: null,
// This option allows use of a custom test runner
// testRunner: "jasmine2",
// This option sets the URL for the jsdom environment. It is reflected in properties such as location.href
// testURL: "http://localhost",
// Setting this value to "fake" allows the use of fake timers for functions such as "setTimeout"
// timers: "real",
// A map from regular expressions to paths to transformers
// transform: null,
// An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation
// transformIgnorePatterns: [
// "/node_modules/"
// ],
// An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them
// unmockedModulePathPatterns: undefined,
// Indicates whether each individual test should be reported during the run
// verbose: null,
// An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode
// watchPathIgnorePatterns: [],
// Whether to use watchman for file crawling
// watchman: true,
};
生成 测试覆盖率 报告命令: npx jest --coverage, 生成 coverage 文件夹。
Jest 使用方式 ES Module
由于 jest 使用的是 node 环境,所以不支持 import 和 export 的方式导入导出文件,因此我们需要配置一下babel让其把 ESModule 语法转为 CommonJS 语法。
1、首先安装babel: npm install @babel/core@7.4.5 @babel/preset-env@7.4.5 -D
2、在项目根目录下创建 .babelrc文件
{
// babel 使用哪些插件的集合, 帮助我们对代码进行转化
// preset的参数是一个数组,数组中也可以是preset
"presets": [
["@babel/preset-env", {
"targets": {
// 根据当前机器的node的版本号,结合@babel/preset-env,对代码进行转化
// 当前 node 不支持 import/export语法,转为CommonJS的模块语法
"node": "current"
}
}]
]
}
3、main.js 文件代码:
export function add(a, b) {
return a + b
}
export function mutil(c, d) {
return c * d
}
// module.exports = {
// add,
// mutil
// }
4、main.test.js 文件语法
// const main = require('./main')
import {add, mutil} from './main'
test('3 + 4 等于 7', () => {
expect(add(3, 4)).toBe(7)
})
test('3 * 4 等于 12', () => {
expect(mutil(3, 4)).toBe(12)
})
5、运行 npm run test
当运行 npm run jest 时,jest 会调用内部的插件 babel-jest, 监测当前环境下是否安装了 babel-core, 如果安装了 babel-core,就去取 .babelrc 的配置,在运行测试之前,结合babel, 先把你的代码做一次转化,例如(把 import 转化为 require),最后运行转化过的测试用例代码。
Jest 中的匹配器
每次修改测试用例,都要手动运行 npm run test很麻烦,可以在 package.json 中配置监听。
"scripts": {
"test": "jest --watchAll"
},
>和真假相关的匹配器
toBe
test('测试 10 与 10 匹配', () => {
// toBe 匹配器 matchers
expect(10).toBe(10)
})
toEqual
test('测试内容匹配', () => {
// toEqual 匹配器 matchers
const obj = {a: 10}
expect(obj).toEqual({a: 10})
})
匹配内容是否相等
toBeNull
test('测试toBeNull匹配', () => {
// toEqual 匹配器 matchers
const obj = null
expect(obj).toBeNull()
})
toBeUndefined
test('测试toBeUndefined匹配', () => {
const obj = undefined
expect(obj).toBeUndefined()
})
toBeDefined
test('测试toBeDefined匹配', () => {
const obj = null
expect(obj).toBeDefined()
})
toBeTruthy
test('测试toBeTruthy匹配', () => {
const obj = 1
expect(obj).toBeTruthy()
})
toBeFalsy
test('测试toBeFalsy匹配', () => {
const obj = 0
expect(obj).toBeFalsy()
})
not
test('测试toBeFalsy匹配', () => {
const obj = 0
expect(obj).not.toBeFalsy()
})
toBeGreaterThan
test('测试toBeGreaterThan', () => {
const a = 10
expect(a).toBeGreaterThan(9)
})
toBeGreaterThanOrEqual
test('测试toBeGreaterThanOrEqual', () => {
const a = 10
expect(a).toBeGreaterThanOrEqual(9)
})
>和数字相关的匹配器
toBeLessThan
test('测试toBeLessThan', () => {
const a = 10
expect(a).toBeLessThan(19)
})
toBeLessThanOrEqual
test('测试toBeLessThanOrEqual', () => {
const a = 10
expect(a).toBeLessThanOrEqual(11)
})
toBeCloseTo
test('测试toBeCloseTo', () => {
const a = 0.1
const b = 0.2
expect(a + b).toBeCloseTo(0.3)
})
>和字符串相关的匹配器
toMatch
test('测试toMatch', () => {
const str = 'abc'
expect(str).toMatch(/ab/)
})
>和集合相关的匹配器
toContain
test('测试toContain', () => {
// Array
const arr = ['a','b','c']
// Set
const data = new Set(...arr)
expect(data).toContain('a')
})
>和异常相关的匹配器
toThrow
const throwNewError = ()=> {
throw new Error('this is a new error')
}
test('测试toThrow', () => {
expect(throwNewError).toThrow()
})
Jest 命令行工具的使用
"scripts": {
"test": "jest --watchAll"
},
all 模式: 任何一个测试用例发生了改变,都会把所有的测试用例重新测试一次
"scripts": {
"test": "jest --watch"
},
f 模式: 当修改一个测试文件的时候,只会对之前没有通过的测试用例再次测试
o 模式: 当更改一个文件的时候,只去测试该文件里修改的测试用例,注意要和 git 管理的项目一起使用,因为用了git 才会知道上次修改和这次修改的内容的差异,对有差异的文件中的测试用例进行测试。
p 模式: 过滤文件名,包含(例如: demo.test.js、demo2.test.js、)这样文件名中的测试用例。
t 模式: 根据测试用例的名字,过滤一下我们想要执行的测试用例,也叫filter模式。
q 模式: 退出测试环境。
enter模式: 重新执行一次被改变文件中的测试用例。
异步代码测试方法
回调类型异步函数的测试
import axios from 'axios'
export const fetchData = (fn) => {
axios.get('http://www.dell-lee.com/react/api/demo.json').then(response => {
fn(response.data)
})
}
import { fetchData } from "./fetchData";
test('fetchData 接口返回对象 { success: true }', (done) => {
fetchData(data => {
expect(data).toEqual({
success: true
})
done()
})
})
方式一
import axios from 'axios'
export const fetchData = (fn) => {
axios.get('http://www.dell-lee.com/react/api/demo.json')
}
import { fetchData } from "./fetchData";
// 测试成功的情况
test('fetchData 接口返回对象 { success: true }', () => {
return fetchData().then(response => {
expect(response.data).toEqual({
success: true
})
})
})
// 测试失败的时候
test('fetchData 返回结果为 404', () => {
// expect至少执行一次
expect.assertions(1);
// 执行 catch时,需要加上 expect.assertions(次数)
return fetchData().catch(e => {
expect(e.toString().indexOf('404') > -1).toBe(true)
})
})
方式二
import axios from 'axios'
export const fetchData = () => {
return axios.get('http://www.dell-lee.com/react/api/demo.json')
}
// 测试成功的情况
test('fetchData 返回结果为 { success: true }', () => {
return expect(fetchData()).resolves.toMatchObject({
data: {
success: true
}
})
})
// 测试失败的时候
test('fetchData 返回结果为 404', () => {
return expect(fetchData()).rejects.toThrow();
})
方式三
import axios from 'axios'
export const fetchData = () => {
return axios.get('http://www.dell-lee.com/react/api/demo.json')
}
// 测试成功的情况
test('fetchData 返回结果为 { success: true }', async () => {
await expect(fetchData()).resolves.toMatchObject({
data: {
success: true
}
})
})
// 测试失败的时候
test('fetchData 返回结果为 404', async () => {
await expect(fetchData()).rejects.toThrow();
})
方式四
import axios from 'axios'
export const fetchData = () => {
return axios.get('http://www.dell-lee.com/react/api/demo.json')
}
// 测试成功的情况
test('fetchData 返回结果为 { success: true }', async () => {
const response = await fetchData();
expect(response.data).toEqual({
success: true
})
})
test('fetchData 返回结果为 404', async () => {
expect.assertions(1);
try {
await fetchData();
} catch(e) {
expect(e.toString()).toEqual('Error: Request fail with status code')
}
})
Jest 钩子函数
beforeAll: 执行测试用例之前
beforeEach: 每个测试用例执行之前,都会执行一次
afterEach: 每个测试用例执行之后,都会执行一次
afterAll: 所有的测试用例结束之后执行
describe 分组
import Counter from "./Counter";
describe('Counter 的测试代码', () => {
let counter = null;
// 执行测试用例之前
beforeAll(() => {
console.log('beforeAll')
});
// 每个测试用例执行之前,都会执行一次
beforeEach(() => {
counter = new Counter();
console.log('beforeEach')
})
// 每个测试用例执行之后,都会执行一次
afterEach(() => {
console.log('afterEach')
})
// 所有的测试用例结束之后执行
afterAll(()=> {
console.log('afterAll')
})
describe('测试增加相关的代码', () => {
test('测试 Counter 中的 addOne 方法', () => {
counter.addOne();
expect(counter.number).toBe(1);
});
test('测试 Counter 中的 addTwo 方法', () => {
counter.addTwo();
expect(counter.number).toBe(2);
});
})
describe('测试减少相关的代码', () => {
test('测试 Counter 中的 minusOne 方法', () => {
counter.minusOne();
expect(counter.number).toBe(-1);
});
test('测试 Counter 中的 minusTwo 方法', () => {
counter.minusTwo();
expect(counter.number).toBe(-2);
});
})
})
export default class Counter {
constructor() {
this.number = 0
}
addOne() {
this.number += 1;
}
addTwo() {
this.number += 2;
}
minusOne() {
this.number -= 1;
}
minusTwo() {
this.number -= 2;
}
}
钩子函数的作用域
先执行外部 describe 中的钩子,然后再执行内部 describe 中的钩子;
只执行指定的test,使用 test.only
Jest 中的 Mock
mock函数作用一: 捕获函数的调用和返回结果,以及this指向和调用顺序
export const runCallback = (callback) => {
callback();
}
import { runCallback } from "./demo";
test('测试 runCallBack', () => {
// mock 函数,捕获函数的调用
const func = jest.fn();
runCallback(func);
// func 函数是否被调用
expect(func).toBeCalled();
})
jest.fn(): mock函数属性解释
通过
jest.fn()生成的函数,会有一个 mock属性 ,属性里有一个
calls: 指被调用多少次,以及每次调用它时传递的参数。
results: 指被调用多少次,以及每次它执行的返回的结果。
instances: 指被调用多少次,以及每次调用this的指向。
invocationCallOrder: 该函数可以被传入同一个方法,或者传入不同的方法中。
Demo2: 函数有参数使用
export const runCallback = (callback) => {
callback('abc');
}
import { runCallback } from "./demo";
test('测试 runCallBack', () => {
// mock 函数,捕获函数的调用
const func = jest.fn();
// 函数被调用了三次
runCallback(func)
runCallback(func);
runCallback(func);
// 判断函数参数的长度是否为3
expect(func.mock.calls.length).toBe(3);
console.log(func.mock)
})
mock函数的作用二: 自由设置返回结果
import { runCallback } from "./demo";
test('测试 runCallBack', () => {
// mock 函数,捕获函数的调用
const func = jest.fn();
// 自由改变设置返回结果
func.mockReturnValue('测试1')
runCallback(func)
// 判断函数的参数是否是 'abc'
expect(func.mock.calls[0]).toEqual(['abc'])
console.log(func.mock)
})
Demo4: 创建对象
export const createObject = (classItem) => {
new classItem()
}
test.only('测试 createObject', () => {
const func = jest.fn();
createObject(func);
console.log(func.mock);
})
mock函数作用三: 改变函数的内部实现
export const getData = () => {
return axios.get('/api').then(res => res.data);
}
import axios from 'axios'
jest.mock('axios')
test('模拟axios请求', async () => {
// 设置 axios 请求数据为 {data: 'hello'}, 不发送真实的请求
axios.get.mockResolvedValue({data: 'hello'})
await getData().then(data => {
expect(data).toBe('hello');
})
})
mock 其他语法
设置返回值
// mock 函数,捕获函数的调用
const func = jest.fn(() => {
console.log('测试')
return 'Lee'
});
等价于:
// func.mockReturnValue('测试1')
func.mockImplementation(() => {
console.log('测试')
return 'Lee'
})
// 自由改变设置返回结果
func.mockReturnValue('测试1')
func.mockReturnValueOnce('测试2')
func.mockImplementation(() => {
console.log('测试3')
return 'Lee'
})
func.mockImplementationOnce(() => {
console.log('测试4')
return 'Bee'
})
mockReturnValue: 设置返回值
mockReturnValueOnce: 只设置一次返回值
mockImplementation: 设置返回值
mockImplementationOnce: 只设置一次返回值
Snapshot 快照测试
u 命令更新所有快照
export const getData = () => {
return {
status: 400,
name: 'Lisa'
}
}
import {getData} from './demo'
test('测试快照', () => {
expect(getData()).toMatchSnapshot();
})
执行jest命令后,测试成功,在目录下生成一个文件夹 __snapshots__, 里面保存的快照
当我修改getData中的参数时:
return {
status: 400,
name: 'Lisa',
age: 19
}
}
测试报错:是因为快照没有被更新导致的
红框中的u命令,可以解决报错,更新快照
i 命令一步一步更新快照, s 命令跳过
export const getData = () => {
return {
status: 400,
name: 'Lisa',
age: 19
}
}
export const getData2 = () => {
return {
status: 400,
name: 'Lisa',
age: 19
}
}
import {getData, getData2} from './demo'
test('测试快照', () => {
expect(getData()).toMatchSnapshot();
})
test('测试快照2', () => {
expect(getData2()).toMatchSnapshot();
})
修改数据后报错:
按 w 选择命令
i 命令:一个一个解决报错
行内快照
安装命令: npm install prettier@1.18.2 --save
回车之后会把快照的内容放在测试用例的第二个参数中:
test("行内测试快照", () => {
expect(getData2()).toMatchInlineSnapshot();
});
test("行内测试快照", () => {
expect(getData2()).toMatchInlineSnapshot(`
Object {
"age": 22,
"name": "Lisa",
"status": 400,
}
`);
});
Vue 中的 TDD 与 单元测试
TDD(Test Driver Development 测试驱动开发)
先编写测试用例,然后编写测试代码
创建vue-jest项目
使用脚手架创建项目:vue create jest-vue
配置后安装打开项目,生成项目目录如下:
jest.config.js 配置文件
module.exports = {
moduleFileExtensions: [ // 测试的文件类型
'js',
'jsx',
'json',
// tell Jest to handle *.vue files
'vue',
'ts',
'tsx'
],
transform: { // 转化方式
// process *.vue files with vue-jest
'^.+\\.vue$': require.resolve('vue-jest'),
'.+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$':
require.resolve('jest-transform-stub'),
'^.+\\.jsx?$': require.resolve('babel-jest'),
'^.+\\.tsx?$': require.resolve('ts-jest'),
},
transformIgnorePatterns: ['/node_modules/'], // 转化时忽略 node_modules
// support the same @ -> src alias mapping in source code
moduleNameMapper: { // @符号 表示当前项目下的src
'^@/(.*)$': '<rootDir>/src/$1'
},
testEnvironment: 'jest-environment-jsdom-fifteen',
// serializer for snapshots
snapshotSerializers: [ // 快照的配置
'jest-serializer-vue'
],
testMatch: [ // 默认测试文件
'**/tests/unit/**/*.spec.[jt]s?(x)',
'**/__tests__/*.[jt]s?(x)'
],
// https://github.com/facebook/jest/issues/6766
testURL: 'http://localhost/',
watchPlugins: [
require.resolve('jest-watch-typeahead/filename'),
require.resolve('jest-watch-typeahead/testname')
],
globals: {
'ts-jest': {
babelConfig: true
}
}
}
不使用 @vue/test-utils
import Vue from 'vue'
import HelloWorld from '@/components/HelloWorld'
describe('HelloWorld.vue', () => {
it('renders props.msg when passed', () => {
// 创建根节点
const root = document.createElement('div')
root.className = 'root'
document.body.appendChild(root)
// 创建Vue对象
new Vue({
render: h => h(HelloWorld, {
props: {
msg: 'hello world !!!'
}
})
}).$mount('.root')
console.log(document.body.innerHTML)
expect(document.getElementsByClassName('hello').length).toBe(1)
})
})
代码写法比较复杂,而且功能多的时候,测试不全面,代码复杂不好写,所以使用 @vue/test-utils, 简化测试代码。
使用 @vue/test-utils
import HelloWorld from '@/components/HelloWorld'
import { shallowMount } from '@vue/test-utils'
describe('HelloWorld.vue', () => {
it('renders props.msg when passed', () => {
const msg = 'new message'
// shallowMount:浅渲染,适合单元测试,只渲染HelloWorld组件,不渲染他得子组件
// mount: 深度渲染,适合集成测试,连同子组件一起渲染
const wrapper = shallowMount(HelloWorld, {
propsData: { msg }
})
expect(wrapper.text()).toMatch(msg)
})
})
shallowMount:浅渲染,适合单元测试,只渲染HelloWorld组件,不渲染他得子组件
mount: 深度渲染,适合集成测试,连同子组件一起渲染
除此之外,还有 render、renderToString等方法,更多的方法可以去查看官网💁
input 框测试用例
逻辑: 没输入 inputValue 之前 input 为空,输入inputValue 后提交内容,然后inputVlaue清空。
<template>
<div class="header">
<input
data-test='input'
v-model="inputValue"
@keyup.enter="addTodoItem"
>
</div>
</template>
<script>
export default {
name: 'Header',
data () {
return {
inputValue: ''
}
},
methods: {
addTodoItem () {
if (this.inputValue) {
this.$emit('add', this.inputValue)
this.inputValue = ''
}
}
}
}
</script>
/* eslint-disable no-undef */
import { shallowMount } from '@vue/test-utils'
import Header from '../../components/Header.vue'
describe('测试input', () => {
it('Header是否存在input框', () => {
const wrappper = shallowMount(Header)
const input = wrappper.find('[data-test="input"]')
expect(input.exists()).toBe(true)
})
it('Header中input 初始内容为空', () => {
const wrappper = shallowMount(Header)
const inputValue = wrappper.vm.$data.inputValue
expect(inputValue).toBe('')
})
it('Header中input 框中值发生变化,数据应该跟着改变', () => {
const wrappper = shallowMount(Header)
const input = wrappper.find('[data-test="input"]')
input.setValue('dell lee')
const inputValue = wrappper.vm.$data.inputValue
expect(inputValue).toBe('dell lee')
})
it('Header中input 框输入回车,无内容时,无反应', () => {
const wrappper = shallowMount(Header)
const input = wrappper.find('[data-test="input"]')
input.setValue('')
input.trigger('keyup.enter')
expect(wrappper.emitted().add).toBeFalsy()
})
it('Header中input 框输入回车,有内容时,向外触发事件,同时清空 inputValue', () => {
const wrappper = shallowMount(Header)
const input = wrappper.find('[data-test="input"]')
input.setValue('dell lee')
input.trigger('keyup.enter')
expect(wrappper.emitted().add).toBeTruthy()
expect(wrappper.vm.$data.inputValue).toBe('')
})
})
TodoList 测试用例
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<Header @add = 'addItem'/>
<ul>
<li v-for="(item, index) in undoList" :key="index">
{{item}}
</li>
</ul>
</div>
</template>
<script>
import Header from './components/Header.vue'
export default {
name: 'TodoList',
components: {
Header
},
props: ['msg'],
data () {
return {
undoList: []
}
},
methods: {
addItem (val) {
this.undoList.push(val)
console.log(this.undoList)
}
}
}
</script>
/* eslint-disable no-undef */
import TodoList from '../../TodoList.vue'
import { shallowMount } from '@vue/test-utils'
import Header from '../../components/Header.vue'
describe('测试todoList分组', () => {
it('todoList 初始化有一个 undoList', () => {
const wrappper = shallowMount(TodoList)
const undoList = wrappper.vm.$data.undoList
expect(undoList).toEqual([])
})
it('todoList 监听到 Header 的 add 事件时,会增加一个内容', () => {
const content = 'dell lee'
const wrappper = shallowMount(TodoList)
const header = wrappper.find(Header)
header.vm.$emit('add', content)
const undoList = wrappper.vm.$data.undoList
expect(undoList).toEqual([content])
})
})
CodeCoverage 测试覆盖率
参考官网配置, 在 jest.config.js 文件中配置这两行代码:
collectCoverage: true,
collectCoverageFrom: ['**/*.{vue}'] // 只针对.vue后缀的文件生成测试报告
在 package.json 中的 scripts 中配置命令:
"test:cov": "vue-cli-service test:unit --coverage",
在 控制台输入命令: npm run test:cov, 在根目录下生成 coverage 文件夹,打开 index.html 文件即可看到测试覆盖率:
总结
Vue 中的 BDD 与 集成测试
BDD (Behavior Driven development)
先写代码,后编写测试用例