什么是自动化测试
自动化测试是把以人为驱动的测试行为转化为程序执行的一种过程。通常,在需求确定以后,测试会编写测试用例,然后,根据测试用例中描述的规程一步步执行测试,得到实际结果与期望结果的比较。在此过程中,为了节省人力、时间或硬件资源,提高测试效率,便引入了自动化测试的概念。
前端自动化测试
高效的测试方法可以加快开发速度,提高代码质量,尽早发现并去除代码中的 BUG,前端实现自动化测试。通常有两种方案可以实现前端自动化测试
unit-test
(单元测试):对应测试中的白盒测试。站在程序员的角度,在测试框架中执行代码,并且给出预期输出,然后对实际输出和预期输出进行比较。以此来判断是否通过测试。e2e-test
(端到端测试):对应测试中的黑盒测试。站在用户的角度,测试框架会模拟用户的行为,例如点击,输入等操作。然后观察页面中的元素展现形式是否和预期一致。以此来判断是否通过测试
两者的存在的意义
unit-test
(`单元测试)是程序员写好自己的逻辑后可以很容易的测试自己的逻辑返回的是不是都正确。e2e-test
(端到端测试)是测试所有的需求是不是都可以正确的完成,而且最重要的是在代码重构,js 改动很多之后,对需求进行测试的时候测试代码是不需要改变的,你也不用担心在重构后不能达到的需求。
测试工具
前端测试工具也和前端的框架一样纷繁复杂,其中常见的测试工具,大致可分为测试框架、断言库、测试覆盖率工具等几类。我们先来大致了解下它们:
测试框架
测试框架的作用是提供一些方便的语法来描述测试用例,以及对用例进行分组。
测试框架可分为两种: TDD (测试驱动开发)和 BDD (行为驱动开发)
常见的测试框架有 Jasmine
, Mocha
以及本次分享要介绍的 Jest
。
断言库
断言库主要提供语义化方法,用于对参与测试的值做各种各样的判断。这些语义化方法会返回测试的结果,要么成功、要么失败。常见的断言库有 Should.js
, Chai.js
等。
测试覆盖率工具
用于统计测试用例对代码的测试情况,生成相应的报表,比如 istanbul
。
为什么选择 Jest
Jest 是 Facebook 出品的一个测试框架,相对其他测试框架,其有几大特点:
- 内置了常用的测试工具,比如自带断言、测试覆盖率工具,实现了开箱即用。
- 作为一个面向前端的测试框架, Jest 可以利用其特有的快照功能,通过比对 UI 代码生成的快照文件,实现对 Vue、React 等框架的自动测试。
- Jest 的测试用例是并行执行的,而且只执行发生改变的文件所对应的测试,测试速度快。
jest 框架
jest 安装以及简单配置
1、安装
npm install jest -D
2、在 package.json 中指定 test 脚本
"scripts": {
"test": "jest"
}
3、jest 配置文件
npx jest --init
基本使用
jest 自动化测试,提供单元测试(模块测试)、集成测试(多个模块测试),因此使用 jest,一定要引入模块的概念,测试的一定是模块
用例的表示
表示测试用例是一个测试框架提供的最基本的 API, 用 test()函数来描述一个测试用例,举个简单的例子:
// math.js
export function add(a, b) {
return a + b;
}
//math.test.js
import { add } from "./math";
test("add方法", () => {
expect(add(1, 2)).toBe(3);
});
其中 toBe(‘3)
便是一句断言( Jest 管它叫 matcher ),写完了用例,运行在项目目录下执行npm test
,即可看到测试结果,
也可以开启监听 -
,不用每次手动执行
异步模块的测试
- 回调
//async.js
export const fetchDataCallback = (fn) => {
axios.get("http://www.dell-lee.com/react/api/demo.json").then((res) => {
fn(res.data);
});
};
//async.test.js
//不要这样写!此测试就在没有调用回调函数前结束
test("fetchDataCallback", () => {
fetchDataCallback((res) => {
expect(res).toEqual({
success: true,
});
});
});
//执行done()之后运行测试用例
test("fetchDataCallback", (done) => {
fetchDataCallback((res) => {
try {
expect(res).toEqual({
success: true,
});
done();
} catch (err) {
done(err);
}
});
});
- promise
//fetchData
export const fetchData = () => {
return axios.get("http://www.dell-lee.com/react/api/demo.json");
};
//fetchData.test.js
//不要忘记return,否则promise解决之前,测试就已经被视为已经完成
test("fetchData", () => {
return fetchData().then((res) => {
expect(res.data).toEqual({
success: true,
});
});
});
使用 .resolves 匹配器,Jest 将等待此 Promise 解决
test("fetchData", () => {
return expect(fetchData()).resolves.toMatchObject({
data: {
success: true,
},
});
});
使用 async 和 await
test("fetchData", async () => {
const res = await fetchData();
expect(res.data).toEqual({
success: true,
});
});
组合使用 async/await 和.resolves 匹配器
test("fetchData", async () => {
await expect(fetchData()).resolves.toMatchObject({
data: {
success: true,
},
});
});
钩子函数
假如有如下的一个场景:
export default class Counter {
constructor() {
this.number = 0;
}
addOne() {
this.number += 1;
}
minusOne() {
this.number -= 1;
}
}
这样一个类,对他写测试用例
import Counter from "./hook";
let counter = new Counter();
test("addOne,", () => {
counter.addOne();
expect(counter.number).toBe(1);
});
test("minusOne,", () => {
counter.minusOne();
expect(counter.number).toBe(-1);
});
假如这样运行,会发现每次都通不过,这是因为在第一个测试用例执行之后,number
属性已经从 0 变成 1,执行第二个用例,number
就 1 的基础上减 1,两个用例互相影响,jest 提供了钩子函数来解决这个问题
let counter = null;
beforeEach(() => {
counter = new Counter();
});
test("addOne,", () => {
counter.addOne();
expect(counter.number).toBe(1);
});
test("minusOne,", () => {
counter.minusOne();
expect(counter.number).toBe(-1);
});
除此之外,jest 还提供其他钩子函数
//所有用例执行之前
beforeAll(() => {});
//每个用例执行之前
beforeEach(() => {});
//每个用例执行之后
afterEach(() => {});
//所有用例执行之后
afterAll(() => {});
describe
用例分组
钩子函数作用域,每一个describe
都可以有自己的钩子函数,执行的顺序是,先执行外层的钩子函数,在执行内层的钩子函数
beforeAll(() => {
});
describe("分组1", () => {
beforeEach(() => {});
test("addOne,", () => {
counter.addOne();
expect(counter.number).toBe(1);
});
test("addTwo,", () => {
counter.addTwo();
expect(counter.number).toBe(2);
});
});
describe("分组2", () => {
beforeEach(() => {});
test("minusOne,", () => {
console.log("minusOne");
counter.minusOne();
expect(counter.number).toBe(-1);
});
test("minusTwo,", () => {
counter.minusTwo();
expect(counter.number).toBe(-2);
});
});
});
jest 中的 mock
- mock 函数 假如这样一个模块,传入一个 callback,然后执行 callback
export const runCallback = (callback) => {
callback();
};
想当然我们写一个回调函数,返回个hello
字符串
test("runCallback", () => {
const func = () => "hello";
expect(runCallback(func)).toBe("hello");
});
运行测试用例,结果报错,这是因为,在原模块中没有返回 callback 的执行结果,如果为了测试要去手动修改代码,显然不太合适,jest 提供函数 mock,用法如下:
test("returnCallback", () => {
//创建mock函数
const func = jest.fn();
//模拟函数返回值
func.mockReturnValue("callbackDone");
returnCallback(func);
expect(func.mock.results[0].value).toBe("callbackDone");
});
作用: 捕获函数的调用次数和返回结果,自由设置返回结果
- mock Ajax ajax 请求的结果应该是后端测试的内容,前端只需要关注请求是否发出,jest 提供对 ajax 的 mock,模拟 ajax 请求而不会真正发出请求,能提高了自动化测试的效率
//ajax
export const getData = () => {
return axios.get("/api").then((res) => res.data);
};
//test
import Axios from "axios";
//mock axios,不会真正发送ajax请求
jest.mock("axios");
test("getData", async () => {
//模拟ajax请求返回结果
Axios.get.mockResolvedValue({ data: "hello" });
await getData().then((data) => {
expect(data).toBe("hello");
});
});
- Timer Mocks 对使用延时器模块进行测试,由于延时时间不一定,每次测试都要等待,影响测试效率,jest 能够模拟延时器,加快测试用例执行
export const timer = (callback) => {
setTimeout(() => {
callback();
}, 3000);
};
jest.useFakeTimers();
test("timer", () => {
const fn = jest.fn();
timer(fn);
//快进3000ms
jest.advanceTimersByTime(3000);
expect(fn).toHaveBeenCalledTimes(1);
});
- ES6 Class Mocks 有一个 Es6 class 工具类,内部方法很复杂
class Util {
init() {
return 1;
//.....
}
a() {
return 2;
//...
}
b() {
return 3;
//....
}
}
export default Util;
有一个模块引用工具类中的方法
import Util from "./util";
export const demoFun = () => {
const util = new Util();
util.a();
util.b();
};
我们要对这个模块进行测试, 通过 jest 可以对工具类进行 mock,简化外部模块,方便单元测试
jest.mock("./util");
//jest.mock发现Util是一个类,会自动把类的构造函数和方法变成jest.fn()
//const Util = jest.fn();
//Util.init = jest.fn();
//Util.a = jest.fn();
//Util.b = jest.fn();
import Util from "./util";
import { demoFun } from "./demoFun";
test("测试demoFun", () => {
demoFun();
expect(Util).toHaveBeenCalled();
expect(Util.mock.instances[0].a).toHaveBeenCalled();
expect(Util.mock.instances[0].b).toHaveBeenCalled();
});
jest 中的 snapShoot 快照
下面是对一个配置文件做测试
//config.js
export const generateConfig = () => {
return {
server: "http://localhost",
port: 8080,
};
};
//config.test.js
import { generateConfig } from "./generateConfig";
test("toMatchSnapshot", () => {
expect(generateConfig()).toEqual({
server: "http://localhost",
port: 8080,
});
});
上面的测试用例能够通过,但是当配置文件修改时,要同步测试用例,jest 提供 snapShoot 快照对配置文件测试
- 生成单独的快照文件
test("toMatchSnapshot", () => {
expect(generateConfig()).toMatchSnapshot();
});
- 行内快照,无独立快照文件,需要先安装 pretter
npm install pretter --save
test("toMatchInlineSnapshot", () => {
expect(generateConfig()).toMatchInlineSnapshot({
date: expect.any(Date),
});
});
生成测试覆盖率报告
npx jest --coverage
Vue 单元测试
单元测试是对应用程序最小的单元运行测试的过程。通常是函数,但在 VUE 中,组件也是单元,因为组件的本质就是函数。
组件的单元测试
单元是应用中最小的可测试部分。在 VUE 中,组件与函数都是可测试的单元。
搭载测试环境
对于新项目来说,可以使用VUE-CLI
工具创建项目
只需要在选择配置时选Unit Testing
单元测试,并选择JEST作为测试框架即可
而对于现有项目而言(针对@vue/cli)想增加jest测试模块。运行以下命令行就会帮我们去安装jest模块。
vue add unit-jest
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!