有时候我们需要不发送请求就能完成前端的业务逻辑测试, 而许多的业务逻辑都会需要调用到后端的 API 接口那如何能 mock 我们所需要的 data 就是一个问题当我们能有一个良好的测试环境之后, 只要保证后端的接口没有问题, 那我们就可以保证业务逻辑也没有问题
所以我们对 API 的集成测试有以下几个要求
1. 不发送请求, 返回本地假数据
2. 发布前通过 CI 跑 unit test, 通过则发布上线
如何实现?
首先一般我们在 network 部分都会进行封装, 假设在 project 中封装了如下的请求工具
- // http tool
- export default function http() {// some implement}
既然我们不能发送真实请求, 那我们就需要类似能拦截的东西, 拦截也可以通过 mock 代替于是我们可以通过 jest.mock 方法来做
- jest.mock
- // api/http.js
- // real fn
- export default function http() {
- console.log('real');
- //...
- }
- // api/__mocks__/http.js
- // fake fn
- export default function http() {
- console.log('fake');
- }
- // some.test.js
- jest.mock('../http')
- import http from '../http'
- http() // 这里 log 的是 fake, 而不是 real
这个就是 jest.mock 的作用
正事
明白了这个后就好办了项目目录如下:
- -- api
- |-- __mockData__
- |-- user.data.js
- |-- __mocks__
- |-- http.js
- |-- __tests__
- |-- user.test.js
- |-- http.js
- |-- profile // profile 业务模块
- |-- user.js // 获取用户信息
而我们的 fake 文件其实主要做的事情就是根据请求 url,method,status 等, 去读取对应的本地假数据大致如下
- // ./api/__mocks__/http.js
- // 直接读取本地假数据
- let statusCode;
- export function setStatus(code) {
- statusCode = code;
- }
- export default function http({ url = "", data = {}, method ="get" }) {
- return new Promise((resolve, reject) => {
- const lastSlash = url.lastIndexOf("/");
- const module = url.substring(lastSlash + 1);
- const mockData = require(`../__mockData__/${module}.data`).default;
- const result = mockData[`${method.toUpperCase()} ${statusCode}`];
- process.nextTick(
- () => (statusCode === 200 ? resolve(result) : reject(result))
- );
- });
- }
mockData 文件夹则就是放我们的假数据, 在这我们可以假设定义如下数据结构, 来模拟我们的 response
- // ./api/__mockData__/user.js
- export default {
- 'GET 200': {
- code: 0,
- msg: 'ok',
- data: {
- username: '二哲',
- age: 18,
- },
- },
- 'POST 200': {
- code: 0,
- msg: 'xxx',
- },
- 'GET 400': {
- msg: 'invald params',
- code: -1,
- },
- 'GET 401': {},
- };
最后看下我们的 unit test 如何写
- // ./api/__test__/user.test.js
- jest.mock('../http') // jest 会自动搜索目录下的 __mocks__里的文件
- import http from '../http';
- describe('user api test', () => {
- it("user GET should be 200", async () => {
- setStatus(200);
- const result = await http({
- url,
- method: "get"
- });
- expect(result.data.username).toBe("Kodo");
- });
- })
实现了这个有什么用?
假设 / user 接口返回得数据可能是这样
- {
- "username": "二哲",
- "age": 18,
- }
而我们前端 service 层为 UI 层提供了一个 initUserData 的方法, initUserData 方法里的操作是当 age 为 18, 那就要返回 19
所以我们在 Jest 则可以直接这样测试
- // ./api/__test__/user.test.js
- jest.mock('../http') // jest 会自动搜索目录下的 __mocks__里的文件
- import { setStatus } from './http';
- import { initUserData } from '../user'
- describe('user api test', () => {
- it("if user age is 18, age should be 19", async () => {
- expect.assertions(1);
- setStatus(200);
- const result = await initUserData();
- // console.log(result);
- expect(result.data.age).toBe(19);
- });
- // test catch
- it("initUserData 400", async () => {
- expect.assertions(1);
- setStatus(400);
- const result = await initUserData();
- expect(result.msg).toBe("invald params");
- });
- })
这样我们使用 Jest 就可以完成对业务逻辑的测试, Unit test 在大型项目中非常需要, 每当提交一个 feature 时, 可以跑完所有测试, 会让你非常有安全感, 极大提升了项目的稳定性
TIP
真正的方法 (http), 与 mock 的方法 http, 文件必须同名, 然后放在 mocks 文件夹下即可如果不同名使用 jest.mock() 则会失败
以上例子都在这 jest-api-test
来源: https://juejin.im/post/5a9a68f85188255584536fe8