单元测试框架Jest学习总结
[[toc]]
测试框架
Mocha+chai(断言库)
yarn add mocha chai -D
Jest
yarn add jest -D
适合场景
- 业务比较复杂
- 公司非常注重代码质量,想尽一切办法杜绝线上出bug
- 需要长期维护的项目。它们需要测试来保障代码可维护性、功能的稳定性
- 被多次复用的部分,比如一些通用组件和库函数。因为多处复用,更要保障质量
- 开源项目
首先安装需要的包
基于vue
yarn add jest vue-jest babel-jest @vue/test-utils @types/jest -D
|
注意版本号之间兼容性问题
让Jest支持ES6语法
"@babel/preset-env", { "targets": { "browsers": [ "chrome >= 50" ], "node": "current" }, "modules": "auto" }
|
测试的步骤
- 写测试说明,针对你的每条测试说明测试了什么功能,预期结果是什么。
- 写测试主体,通常是 输入 -> 输出。
- 判断测试结果,拿输出和预期做对比。如果输出和预期相符,则测试通过。反之,不通过。
yarn add jest @types/jest babel-jest babel-core babel-preset-env regenerator-runtime -D
Jest本身是不支持es6的,但是在react中已经配置好babel等,可以直接使用ES6的语法特性进行单元测试
使用方式
toBe() 绝对相等
toEqual() 判断对象或者数组是否相等
toBeNull()只匹配null
toContain()检测数组中是否包含特定某一项
toBeUndefined()只匹配undefined
toBeDefine()与toBeUndefined相反
toBeTruthy()匹配任何if语句为真
toBeFalsy()匹配任何if语句为假
toBeCloseTo(0.3) 浮点数判断相等
数字匹配器
toBeGreaterThan()大于
toBeGreaterThanOrEqual()大于或者等于
toBeLessThan()小于
toBeLessThanOrEqual()小于或等于
package里面关于jest的配置
"scripts": { "test": "jest", "app":"jest /test/app.test.js --watch" "test-watch": "jest --watchAll", "test-with-coverage": "jest --coverage" }, "jest": { "collectCoverage":true, "collectCoverageFrom": [ "src/**/*.{js,jsx,ts,tsx}", "!src/**/*.d.ts" ], "coverageDirectory": "tests/coverage", "resolver": "jest-pnp-resolver", "setupFiles": [ "react-app-polyfill/jsdom" ], "testMatch": [ "<rootDir>/test/**/__tests__/**/*.{js,jsx,ts,tsx}", "<rootDir>/test/**/?(*.)(spec|test).{js,jsx,ts,tsx}" ], "testEnvironment": "jsdom", "testURL": "http://localhost", "transform": { "^.+\\.(js|jsx|ts|tsx)$": "<rootDir>/node_modules/babel-jest", "^.+\\.css$": "<rootDir>/config/jest/cssTransform.js", "^(?!.*\\.(js|jsx|ts|tsx|css|json)$)": "<rootDir>/config/jest/fileTransform.js" }, "transformIgnorePatterns": [ "[/\\\\]node_modules[/\\\\].+\\.(js|jsx|ts|tsx)$", "^.+\\.module\\.(css|sass|scss|less)$" ], "moduleNameMapper": { "^react-native$": "react-native-web", "^.+\\.module\\.(css|sass|scss|less)$": "identity-obj-proxy", "^@(.*)$": "<rootDir>/src$1" }, "moduleFileExtensions": [ "js", "ts", "tsx", "json", "jsx", "node" ] }
|
Jest.config.js
module.exports = { moduleFileExtensions: ["vue", "js", "jsx"], testEnvironment: "jsdom", transform: { "^.+\\.js$": "babel-jest", "^.+\\.vue$": "vue-jest", }, moduleDirectories: ["node_modules"], testRegex: "(/__tests__/.(js|jsx)|(\\.|/)(test|spec))\\.(js|jsx)$", moduleNameMapper: { "^@/(.*)$": "<rootDir>/src/$1", }, };
|
代码覆盖率
可以查看你那些代码没有被覆盖,帮助你发现盲点
- 在命令行中通过 “–coverage” flag 指定
- 在 package.json 中手动配置

%stmts是语句覆盖率(statement coverage):是不是每个语句都执行了?
%Branch分支覆盖率(branch coverage):是不是每个if代码块都执行了?
%Funcs函数覆盖率(function coverage):是不是每个函数都调用了?
%Lines行覆盖率(line coverage):是不是每一行都执行了?
Uncovered Line 是哪行没有被覆盖
专业术语里,把describe包含的块叫做suite,把it/test包含的块叫做specification,也简称为spec,在一个suite里面可以包含多个数量的spec,但是也要注意结构化语义化。
示例
编写测试文件时遵循的命名规范:测试文件的文件名 = 被测试模块名 + .test.js
import axios from 'axios'; export default { fetchUser() { return axios.get('http://jsonplaceholder.typicode.com/users/1') .then(res => res.data) .catch(error => console.log(error)); }, sum(a, b) { return a + b; } }
import functions from '../src/functions'; test('fetchUser() 可以请求到一个含有name属性值为Leanne Graham的对象', () => { expect.assertions(1); return functions.fetchUser() .then(data => { expect(data.name).toBe('Leanne Graham'); }); }); it('fetchUser() 可以请求到一个含有name属性值为Leanne Graham的对象 async -- await', async () => { expect.assertions(1); const data = await functions.fetchUser();
expect(data.name).toBe('Leanne Graham');
}); describe('加法函数测试', () => { it('1加2应该等于3', () => { expect(functions.sum(1, 2)).toBe(3); }); }); test('sum(2 + 2) 等于 4', () => { expect(functions.sum(2, 2)).toBe(4); }); test('sum(2 + 2) 等于 4', () => { expect(functions.sum(2, 2)).not.toBe(1008611); }); test('there is no I in team', () => { expect('team').not.toMatch(/I/); }); test('but there is a “stop” in Christoph', () => { expect('Christoph').toMatch(/stop/); }); test('测试浮点数是否相等', () => { expect(0.003 + 0.01).toBeCloseTo(0.013); }); test('对象判断是否相等', () => { expect({test: "11111"}).toEqual({test: "11111"}); });
describe("筛选数组", () => { test("it should filter by a search term (link)", () => { const input = [ {id: 1, url: "https://www.url1.dev"}, {id: 2, url: "https://www.url2.dev"}, {id: 3, url: "https://www.link3.dev"} ];
const output = [{id: 3, url: "https://www.link3.dev"}];
expect(filterByTerm(input, "link")).toEqual(output);
expect(filterByTerm(input, "LINK")).toEqual(output); }); });
function filterByTerm(inputArr, searchTerm) { const regex = new RegExp(searchTerm, "i"); const a = inputArr.filter(function (arrayElement) { return arrayElement.url.match(regex); }); console.log(a); return a }
|
vue中的测试案例
<template> <div> <slot></slot> </div> </template>
// test.spec.js import { shallowMount, mount } from "@vue/test-utils"; import test from "../test.vue"; import Vue from "vue";
describe("test.vue", () => { let wrapper = shallowMount(test, { slots: { default: "测试案例", }, });
it("设置slot", () => { return Vue.nextTick().then(function() { expect(wrapper.text()).toBe("测试案例"); }); }); });
|
未完待续…
参考文档
*ReactTestUtils
参考文档
参考文档2
jest 别名
Jest 入门教程