antd-pro1.0使用jest对react组件进行单元测试

前言

基于React+Ant Design(以下用Antd表示)的项目,在对于自己封装的,或者基于Antd封装的公共组件的自动化测试技术的选型和实践。

背景

随着前端项目越来越大,业务逻辑日益繁杂,协同开发的同事也越来越多,迭代频繁,许多页面有一些相似的功能,会复用一些组件,这些组件被剥离出来,一般放在component文件夹下,大家共同维护,这时会出现一些常见问题:

  • 从代码层面看,我们必须保证当前组件的质量,即当前业务的正常使用
  • 在新需求下,旧组件如果能满足新需求50%以上的功能,应当升级旧组件满足新需求,同时兼容旧业务
  • 除该组件Owner之外第二人,在修改组件的过程中,避免因为对代码的不熟悉,改出BUG
  • 一个组件多个页面复用,修改后的测试回归任务重

单元测试的好处

  • 测试用例代码跟项目代码完全分离,但是能检验当前组件的质量、健壮性,保证业务能够正常使用
  • 降低项目多处使用的公共组件修改后,测试回归任务重(改一处测多处)的问题
  • 减少不必要的console.log或者debug,提升维护体验
  • 对开发人员来说,写测试用例可以适量提升对代码实现的理解

铭记:测试不是最终目的,尽可能在开发阶段有意识地提高代码质量、规避问题。

React项目单元测试框架Jest

  • 源自Facebook,Jest的一个理念是提供一套完整集成的零配置测试体验,开箱即用。
  • 包含单元测试运行器、断言库、Mock库
  • 内置代码覆盖率报告
  • 可以与TypeScript一同使用

jest针对为实现某些业务逻辑而封装的函数,设定多种可能的输入,判断是否准确地返回了期望值

当前管理平台的简单示例

1.测试对象:

侧边菜单栏组件(./src/componets/SiderMenu.js)中使用到的数据和逻辑处理函数输入输出表现

2.测试前分析:

业务角度分析:这是一个按权限展示的多级侧边主菜单栏。(权限方面需要测试及后端人员进行)
组件实现分析:根据后端提供的数据做菜单展示,如果存在多级数据就多级展示,同时可基于菜单实现路由映射。(jest单元测试)

3.确定测试对象用到的数据和逻辑处理函数

以下两个文件中包含并导出了我们的测试对象组件SiderMenu使用到的函数
**js文件1:./src/componets/_utils/pathTools.js**

// /userinfo/2144/id => ['/userinfo','/useinfo/2144,'/userindo/2144/id']
export function urlToList(url) {
  const urllist = url.split('/').filter(i => i);
  return urllist.map((urlItem, index) => {
    return `/${urllist.slice(0, index + 1).join('/')}`;
  });
}

js文件2:./src/componets/SiderMenu.js

......
......
/**
 * 递归扁平化数据
 * [{path:string},{path:string}] => [path,path2]
 * @param  menu
 */
export const getFlatMenuKeys = menu =>
  menu.reduce((keys, item) => {
    keys.push(item.path);
    if (item.children) {
      return keys.concat(getFlatMenuKeys(item.children));
    }
    return keys;
  }, []);

/**
 * 根据paths查找所有匹配的菜单的keys
 * @param  flatMenuKeys: [/abc, /abc/:id, /abc/:id/info]
 * @param  paths: [/abc, /abc/11, /abc/11/info]
 */
export const getMenuMatchKeys = (flatMenuKeys, paths) =>
  paths.reduce(
    (matchKeys, path) =>
      matchKeys.concat(flatMenuKeys.filter(item => pathToRegexp(item).test(path))),
    []
  );
......
......

4.编写测试用例

在测试组件目录下添加.test.js文件,运行jest将会自动识别并执行.test.js结尾的文件进行测试。

jest提供的定义测试的一些方法:

  • describe: 定义一个测试套件(test suite)
  • it: 定义一个测试(test)
  • beforeEach: 定义一个回调函数在每个测试之前执行
  • expect: 执行一个断言
  • jest.fn(): 创造一个mock函数
    ..................

测试用例:SlideMenu.test.js

// 导入待测试的三个函数
import { urlToList } from '../_utils/pathTools';
import { getFlatMenuKeys, getMenuMatchKeys } from './SiderMenu';

// 先mock一个测试的菜单栏数据作为参数
const menu = [
  {
    path: '/dashboard',
    children: [
      {
        path: '/dashboard/name',
      },
    ],
  },
  {
    path: '/userinfo',
    children: [
      {
        path: '/userinfo/:id',
        children: [
          {
            path: '/userinfo/:id/info',
          },
        ],
      },
    ],
  },
];

// 传入mock的数据给getFlatMenuKeys得到的输出为flatMenuKeys
const flatMenuKeys = getFlatMenuKeys(menu);

// 测试getFlatMenuKeys返回的结果是否符合我们的预期
describe('test convert nested menu to flat menu', () => {
  it('simple menu', () => {
    expect(flatMenuKeys).toEqual([
      '/dashboard',
      '/dashboard/name',
      '/userinfo',
      '/userinfo/:id',
      '/userinfo/:id/info',
    ]);
  });
});

// 测试菜单匹配 模拟多种可能的输入是否符合期望(多种输入对应的输出是否符合预期)
describe('test menu match', () => {
  // 简单的一级菜单应该返回对应的一个一级菜单结果
  it('simple path', () => {
    expect(getMenuMatchKeys(flatMenuKeys, urlToList('/dashboard'))).toEqual(['/dashboard']);
  });
  // 错误或不存在的路径应该返回[]
  it('error path', () => {
    expect(getMenuMatchKeys(flatMenuKeys, urlToList('/dashboardname'))).toEqual([]);
  });
  // 二级菜单应该返回对应的所有菜单keys
  it('Secondary path', () => {
    expect(getMenuMatchKeys(flatMenuKeys, urlToList('/dashboard/name'))).toEqual([
      '/dashboard',
      '/dashboard/name',
    ]);
  });
  // 存在参数的路径应该返回对应的预期
  it('Parameter path', () => {
    expect(getMenuMatchKeys(flatMenuKeys, urlToList('/userinfo/2144'))).toEqual([
      '/userinfo',
      '/userinfo/:id',
    ]);
  });
  // 三级带参数路径
  it('three parameter path', () => {
    expect(getMenuMatchKeys(flatMenuKeys, urlToList('/userinfo/2144/info'))).toEqual([
      '/userinfo',
      '/userinfo/:id',
      '/userinfo/:id/info',
    ]);
  });
});

5.测试操作:

// 因为当前项目是基于roadhog、roadhog将Jest已经隐藏
yarn roadhog test ./src/componets/SiderMenu
// 如果是普通react项目使用jest需要安装
yarn jest test ./src/componets/SiderMenu

更多功能待完善....

上一篇:ios下, 点击时出现黑色块


下一篇:spring Quartz多个定时任务的配置