[前端测试] Jest自动化测试
jest 和 moka 等测试框架原理简单解释
// math.js文件
function add(a, b) {
return a + b;
}
function minus(a, b) {
return a - b;
}
function multi(a, b) {
return a * b;
}
// 我们将测试这个文件写的是否正确
// math.test.js文件
// var result = add(3, 7);
// var expected = 10;
// if (result !== 10) {
// throw new Error(`3 + 7 应该等于 ${expected}, 但是结果却是 ${result}`);
// }
// var result = minus(3, 3);
// var expected = 0;
// if (result !== 0) {
// throw new Error(`3 - 3 应该等于 ${expected}, 但是结果却是 ${result}`);
// }
function expect(result) {
return {
toBe: function (actual) {
if (result !== actual) {
throw new Error(`预期值和实际值不相等 预期${actual} 结果却是${result}`);
}
},
};
}
function test(desc, fn) {
try {
fn();
console.log(`${desc} 通过测试`);
} catch (e) {
console.log(`${desc} 没有通过测试 ${e}`);
}
}
test("测试加法 3+7", () => {
expect(add(3, 7)).toBe(10);
});
test("测试减法 3-3", () => {
expect(minus(3, 3)).toBe(0);
});
test("测试乘法 3*3", () => {
expect(multi(3, 3)).toBe(9);
});
tip
expect 就是返回一个函数, 该函数对比实际值和预期值
test 函数通过 try catch 来检测是否通过测试
使用 Jest 修改自动化测试样例
jest 在前端自动化测试中完成的是单元测试和集成测试
所谓单元测试就是测试一个模块, 集成测试理解成多个模块测试
所以 jest 必须要引入模块
// math.js
function add(a, b) {
return a + b;
}
function minus(a, b) {
return a - b;
}
function multi(a, b) {
return a * b;
}
try {
// commonJs语法
module.exports = {
add,
minus,
multi,
};
} catch (e) {}
// math.test.js
const math = require("./math.js");
const { add, minus, multi } = math;
test("测试加法 3+7", () => {
expect(add(3, 7)).toBe(10);
});
test("测试减法 3-3", () => {
expect(minus(3, 3)).toBe(0);
});
test("测试乘法 3*3", () => {
expect(multi(3, 3)).toBe(9);
});
jest 与 babel 结合
首先安装 babel/core 核心库, 再安装 babel/preset-env, 在目录中创建 babelrc 文件
{
// 插件用哪些集合帮助代码转换
"presets": [
[
"@babel/preset-env",
{
// 根据当前机器的node环境结合babel/preset-env对现在的代码做转换
// 当代码中使用import语句的时候babel使用babel/preset-env帮助我们对
// import 语句进行转换, 转换成commonjs的语法, 这样node和jest就可以运行了
"targets": {
"node": "current"
}
}
]
]
}
// 底层机制
// 当运行npm run jest的时候
// jest内部集成了插件,插件叫babel-jest, 它会检测当前环境是否安装了babel或babel/core
// 如果安装了babel/core, 它去拿babelrc里面的配置
// 取得配置后, 它会在运行测试之前, 结合babel先把代码转换一次,比如import变成require语法
// 运行转换过的测试代码
jest 的匹配器
首先修改 package.json 文件自动监听文件变化
// 修改package.json
"scripts": {
// 让jest监听所有测试文件的变化, 文件变化就自动重新测试所有测试用例
"test": "jest --watchAll",
"coverage": "jest --coverage"
},
toBe
toEqual
test("测试对象内容相等", () => {
const a = { one: 1 };
expect(a).toEqual({ one: 1 });
});
真假有关的匹配器
toBeNull
test("测试是否null", () => {
const a = null;
expect(a).toBeNull();
});
toBeUndefined
test("测试toBeUndefined", () => {
const a = undefined;
expect(a).toBeUndefined();
});
toBeDefined
test("toBeDefined", () => {
const a = 11;
expect(a).toBeDefined();
});
toBeTruthy
test("toBeTruthy", () => {
const a = 0;
expect(a).toBeTruthy(); // 不通过
});
toBeFalsy
test("toBeFalsy", () => {
const a = 0;
expect(a).toBeFalsy(); // 通过
});
数字有关的匹配器
toBeGreaterThan
toBeLessThan
toBeGreaterThanOrEqual
toBeCloseTo
test("toBeTruthy", () => {
const a = 0.1;
const b = 0.2;
expect(a + b).toEqual(0.3);
});
// 如果用toEqual是不通过的, 因为浮点不准确0.333333..
// 所以可以用toBeCloseTo
test("toBeTruthy", () => {
const a = 0.1;
const b = 0.2;
expect(a + b).toBeCloseTo(0.3);
});
字符串相关的匹配器
toMatch 是否包含
数组相关的匹配器
toContain 数组里是否包含
异常情况匹配器
toThrow
const throwNewError = () => {
throw new Error("A");
};
// 我们希望throwNewError函数能够抛出异常A, 就用如下方式测试
test("toThrow", () => {
expect(throwNewError).toThrow("A");
});
jest 命令行
Watch Usage
› Press f to run only failed tests. // 只对没通过的用例跑一遍
› Press o to only run tests related to changed files. // 只测试更改的文件
› Press p to filter by a filename regex pattern. // 测试根据正则匹配的文件名
› Press t to filter by a test name regex pattern. // 测试根据正则匹配的名的用例
› Press q to quit watch mode.
› Press Enter to trigger a test run.
异步代码测试
第一种: 回调类型异步函数的测试方式
import axios from "axios";
export const fetchData = (fn) => {
// fetchData接收一个函数,当请求如下地址成功后,获取到response,
// 这个response就是服务器返回的内容, 就调用fn, 并把response传入到fn中
axios.get("http://www.dell-lee.com/react/api/demo.json").then((response) => {
fn(response.data);
});
};
// 那么我们如何测试这个fetchData函数写的对不对?
// 使用fetchData.test.js
// 该文件中引入要测试的函数,也就是fetchData函数
// 然后
test("fetchData 返回结果为 {success: true}", () => {
fetchData((data) => {
expect(data).toEqual({ success: true });
});
});
但是实际上 test 中的回调函数并没有执行, 所以需要借助 done 函数
test("fetchData 返回结果为 {success: true}", (done) => {
fetchData((data) => {
expect(data).toEqual({ success: true });
});
done();
});
第二种 直接返回 promise 的形式
有时 fetchData 这种函数不接收回调, 就是单纯的 promise. 代码如下
export const fetchData = () => {
return axios.get("http://www.dell-lee.com/react/api/demo.json");
};
上面代码具体失败成功怎么做我不管, 只帮你发请求, 请求结束之后自己处理成功和失败的请求
// fetchData()执行后返回的是个promise, 对promise使用then方法
test("fetchData 返回结果为 {success: true}", () => {
return fetchData().then((response) => {
expect(response.data).toEqual({ success: true });
});
});
测试 404
test("fetchData 返回结果为 404", () => {
expect.assertions(1);
return fetchData().catch((e) => {
expect(e.toString().indexOf("404") > -1).toBe(true);
});
});
第三种
test("fetchData 返回结果为 {success: true}", () => {
return expect(fetchData()).resolves.toMatchObject({
data: {
success: true,
},
});
});
test("fetchData 返回结果为 404", () => {
return expect(fetchData()).rejects.toThrow();
});
第四种 await async
test("fetchData 返回结果为 {success: true}", async () => {
await expect(fetchData()).resolves.toMatchObject({
data: {
success: true,
},
});
});
第五种 最清晰的写法
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 failed with status code 404");
}
});
Jest 的钩子函数
jest 的钩子函数就是在 jest 执行过程中某一些具体时刻自动被 jest 调用的函数
测试类
import Counter from "./Counter";
// 不能直接使用类的方法, 要通过实例使用类的方法
const counter = new Counter();
test("测试addOne方法", () => {
counter.addOne();
expect(counter.number).toBe(1);
});
test("测试minusOne方法", () => {
counter.minusOne();
expect(counter.number).toBe(0);
});
上面的写法, 加法和减法都对一个实例产生了作用, 所以不利于测试
所以如果在测试前对一些内容进行初始化的时候, jest 提供了一些钩子函数
比如现在两个测试函数都需要 counter 实例, 改造代码如下
import Counter from "./Counter";
let counter = null;
beforeAll(() => {
console.log("beforeAll");
counter = new Counter();
});
test("测试addOne方法", () => {
console.log("测试addOne方法");
counter.addOne();
expect(counter.number).toBe(1);
});
test("测试minusOne方法", () => {
console.log("测试minusOne方法");
counter.minusOne();
expect(counter.number).toBe(0);
});
beforeAll
afterAll
beforeEach
afterEach
钩子函数的作用域
Jest 的 Mock
jest.fn()
这是一个 mock 函数
toBeCalled()
检测是否被执行过
snapshot 快照测试
toMatchSnapshot
可以用来帮助测试配置文件
定时器模拟
useFakeTimers