Jest单测实践篇

news2024/9/22 15:28:24

快照测试

快照测试在你要确保你的UI没有发生改变的时候非常有用。jest的快照测试为文本测试,第一次执行时存储本次的快照,然后在之后的测试过程中进行文本比对。

toMatchSnapshot() 方法

import React from 'react';
import Link from '../Link.react';
import renderer from 'react-test-renderer';

it('renders correctly', () => {
  const tree = renderer
    .create(<Link page="http://www.facebook.com">Facebook</Link>)
    .toJSON();
  expect(tree).toMatchSnapshot();
});

The snapshot artifact should be committed alongside code changes, and reviewed as part of your code review process.
注意生成的快照文件应该和代码的变更一起被提交和Review。

如何更新快照

有时候快照测试不通过是因为组件更新了,那么快照如何更新呢?

(1)你可以通过jest --updateSnapshot or jest -u 方法来让jest为所有快照测试失败的组件重新生成快照。如果你想精确控制哪些快照被更新,可以用–testNamePattern参数指定正则表达式。

(2) 使用watch模式来更新 jest --watch

在这里插入图片描述
按i可以交互式的更新失败的快照。

更多请参考:
https://jestjs.io/docs/en/snapshot-testing

如何测试回调函数被成功调用

测试回调函数被调用

(1) 使用jest.fn()函数生成模拟函数

const onDropdownVisibleChange = jest.fn();

(2) 在组件的回调中指定该模拟函数为回调函数

const wrapper = mount(
 <Select open onDropdownVisibleChange={onDropdownVisibleChange}>
   <Option value="1">1</Option>
 </Select>,
);

(3-1) 模拟点击后进行断言,toHaveBeenLastCalledWith断言被特定参数调用

wrapper.find('.ant-select').simulate('click');
expect(onDropdownVisibleChange).toHaveBeenLastCalledWith(false);

完整实例:

const onDropdownVisibleChange = jest.fn();
const wrapper = mount(
  <Select open onDropdownVisibleChange={onDropdownVisibleChange}>
  <Option value="1">1</Option>
</Select>,
);
wrapper.find('.ant-select').simulate('click');
expect(onDropdownVisibleChange).toHaveBeenLastCalledWith(false);

(3-2) 模拟点击后进行断言,toHaveBeenCalled断言被调用

const onDropdownVisibleChange = jest.fn();
const wrapper = mount(
  <Select open onDropdownVisibleChange={onDropdownVisibleChange}>
  <Option value="1">1</Option>
</Select>,
);
wrapper.find('.ant-select').simulate('click');
expect(onDropdownVisibleChange).toHaveBeenCalled();

如何测试有时间方法的组件

jest.useFakeTimers: 模拟时间流逝

通常一些本地的时间方法比如setTimeout等不太适合测试环境,因为这些方法会依赖真实的时间流逝。jest可以交换这些时间函数,控制时间的推移,比方说:

beforeAll(() => {
  jest.useFakeTimers();
});

// 或者有多个测试用例使用在每个测试用例执行之前执行
beforeEach(() => {
  jest.useFakeTimers();
});

jest.runAllTimers(): 控制时间运行

还有一些测试用例用于测试某个回调一秒后会被调用:

test('calls the callback after 1 second', () => {
  const timerGame = require('../timerGame');
  const callback = jest.fn();

  timerGame(callback);
	
	// 这个时间点还没有被调用
  expect(callback).not.toBeCalled();

  // 快进,让所有时间回调都执行
  jest.runAllTimers();

  // 现在回调被调用
  expect(callback).toBeCalled();
  expect(callback).toHaveBeenCalledTimes(1);
});

jest.useRealTimers(): 使用真实的时间

在测试过程中可能想要恢复时间正常流逝,可以使用useRealTimers()方法恢复

更多关于时间控制的方法关注:
Timer Mocks · Jest

如何模拟事件进行测试

wrapper.simulate()方法

wrapper.find(‘a’).simulate(‘click’);

如:

class Foo extends React.Component {
  constructor(props) {
    super(props);
    this.state = { count: 0 };
  }

  render() {
    const { count } = this.state;
    return (
      <div>
        <div className={`clicks-${count}`}>
          {count} clicks
        </div>
        <a href="url" onClick={() => { this.setState({ count: count + 1 }); }}>
          Increment
        </a>
      </div>
    );
  }
}

const wrapper = shallow(<Foo />);

expect(wrapper.find('.clicks-0').length).to.equal(1);
wrapper.find('a').simulate('click');
expect(wrapper.find('.clicks-1').length).to.equal(1);

模拟input框输入

wrapper.find('.ant-input').simulate('change', { target: { value: 'test' } });

模拟checkbox change输入

wrapper
      .find('.ant-checkbox-input')
      .at(0)
      .simulate('change', { target: { checked: true } });

如何测试方法被正确的参数调用

比方说antd的一个测试用例是检测用户如果全量引入antd的包,而不是动态引入组件的话,那么会有个提示,怎么验证这个提示被成功展示了呢?

jest.spyOn()方法

(1) 监控对应的方法,如这里是console.warn方法,但是不让他真的输出出来。

const warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});

(2)使用断言toBeCalledWith,被用xxx参数调用

expect(warnSpy).toBeCalledWith(
      'You are using a whole package of antd, please use https://www.npmjs.com/package/babel-plugin-import to reduce app bundle size.',
    );

(3) 恢复

warnSpy.mockRestore();

更多参考:https://jestjs.io/docs/jest-object

如何测试组件的属性与状态是否正确

wrapper.props() 获取当前的组件属性

expect(dropdownWrapper.props().visible).toBe(true);

设置属性

wrapper.setProps({ open: false });

wrapper.instance().state 获取当前对象状态

expect(wrapper.instance().state.status).toBe(0);
expect(wrapper.instance().state.placeholderStyle).toBe(undefined);

如何测试debounce过的方法

如果说组件代码中使用了debounce方法,比方说经常我们需要用debounce去控制请求的发送频率:

import debounce from 'lodash/debounce';
//...
this.fetchData = debounce(this.fetchUserData, this.props.delayTime);

但是在测试用例中怎么去让debounce返回的函数被立即执行呢?毕竟原函数默认是需要delay一段时间后再执行的,答案是可以使用jest的mock方法。

测试步骤

1. 在测试用例中引入debounce

import debounce from 'lodash/debounce';

2. 使用jest.mock运行这个函数,给他加上mock方法

jest.mock('lodash/debounce');

这样 debounce 方法中就会被加上 mockImplementation 以及 mockRestore 方法

3. 在运行测试用例前mock该函数实现,运行测试用例后restore

beforeEach(() => {
  jest.useFakeTimers();
  debounce.mockImplementation(fn => fn);
});

afterEach(() => {
  jest.useRealTimers();
  debounce.mockRestore();
});

比方说在 beforeEach 和 afterEach 中分别调用 mockImplementation 和 mockRestore , fn => fn 的含义是指返回这个函数本身,比方说:

this.fetchData = debounce(this.fetchUserData, this.props.delayTime);

运行完debounce函数后, this.fetchData === this.fetchUserData ,那么调用 this.fetchData 函数的时候请求就会立即执行了。

测试用例案例

一个完整的测试用例的案例参考如下:

import * as React from 'react';
// eslint-disable-next-line import/no-extraneous-dependencies
import { mount } from 'enzyme';
import debounce from 'lodash/debounce';
import UserSearch from '../index';

jest.mock('lodash/debounce');

describe('<UserSearch />', () => {
  beforeEach(() => {
    jest.useFakeTimers();
    debounce.mockImplementation(fn => fn);
  });

  afterEach(() => {
    jest.useRealTimers();
    debounce.mockRestore();
  });

  it('should stop fetching if users empty', done => {
    const wrapper = mount(
      <UserSearch userSearchRequest={() => Promise.resolve([])} />,
    );

    wrapper.find('input').simulate('change', { target: { value: '1' } });
    jest.runAllTimers();

    process.nextTick(() => {
      wrapper.update();
      expect(wrapper.instance().state.fetching).toBe(false);
      done();
    });
  });

});

如何测试返回promise的方法

异步方法通常都具有不确定性,promise的方法也是,但是我们希望在函数调用后立即能够看到组件的执行效果,怎么办呢?答案是使用 process.nextTick() 方法。

比方说我的promise方法是这样:

fetchData = query => {
    const { userSearchRequest } = this.props;
    userSearchRequest &&
      userSearchRequest({ query })
        .then(users => {
         	...
          dataSource = ...;
          this._mounted && this.setState({ dataSource, fetching: false });
        })
        .catch(err => {
          this._mounted && this.setState({ fetching: false });
        });
  };

在promise完成之后setState,我希望去测试setState之后的效果,相应的测试用例如下:

it('should stop fetching if users empty', done => {
    const wrapper = mount(
      <UserSearch userSearchRequest={() => Promise.resolve([])} />,
    );

    wrapper.find('input').simulate('change', { target: { value: '1' } });
    jest.runAllTimers();

    process.nextTick(() => {
      wrapper.update();
      expect(wrapper.instance().state.fetching).toBe(false);
      done();
    });
  });

nextTick 会在目前队列中的所有事件完成之后执行,保证了promise方法此时已经完成执行。

如何测试moment相关方法

在我们的测试当中涉及日期组件的时候,进行snapshot测试会出现snapshot总是不通过的问题,那是因为moment总是获取当前的日期,造成snapshot的变化,为了解决这个问题,我们可以mock日期,让他返回固定的日期。

mockdate

我们使用 mockdate 来帮我们mock 日期相关的方法

tnpm install --save-dev mockdate

然后创建mock方法:

import moment from 'moment';
import MockDate from 'mockdate';

export function setMockDate(dateString = '2017-09-18T03:30:07.795') {
  MockDate.set(moment(dateString));
}

export function resetMockDate() {
  MockDate.reset();
}

最后在测试开始之前调用mock方法即可:

import React from 'react';
import { mount } from 'enzyme';
import toJson from 'enzyme-to-json';
import DateRanger from '..';
import { setMockDate, resetMockDate } from './utils';

describe('DateRanger', () => {
  beforeEach(() => {
    setMockDate();
  });

  afterEach(() => {
    resetMockDate();
  });

  it('should match snapshot', () => {
    const wrapper = mount(<DateRanger />);
    // 快照一致
    expect(toJson(wrapper)).toMatchSnapshot();
    wrapper.unmount();
  });
});

一些组件测试实例的收集测试

某个节点的文案正确:

expect(
      dropdownWrapper
        .find('MenuItem')
        .at(0)
        .text(),
    ).toBe('No Data');

测试各个标签属性设置正确

// 获取DOM节点
const input = wrapper.find('.ant-alert').getDOMNode();
// 2. getAttribute获取属性
expect(input.getAttribute('data-test')).toBe('test-id');

测试被点开的Select按钮,找到展开的dropdown组件并点击或输入

it('should have default notFoundContent', () => {
    const wrapper = mount(<Select mode="multiple" />);
    wrapper.find('.ant-select').simulate('click');
    jest.runAllTimers();
    const dropdownWrapper = mount(
      wrapper
        .find('Trigger')
        .instance()
        .getComponent(),
    );
    expect(dropdownWrapper.find('MenuItem').length).toBe(1);
    expect(
      dropdownWrapper
        .find('MenuItem')
        .at(0)
        .text(),
    ).toBe('No Data');
  });

测试错误

it('should throw error when option value is missing', () => {
    try {
      mount(
        <CheckCard.Group options={[{ value: 'Apple' }, { value: 'Pear' }, { title: 'Orange' }]} />,
      ).unmount();
    } catch (e) {
      expect(e).toBeDefined();
    }
  });

组件测试用例的设计

前言

React组件的测试和nodejs的单元测试有很大不同,我在对组件进行测试的时候,经常迷惑的一个点是我应该测试些什么?下面是在实战过程中总结的一些经验。

测试目标

首先要确定自己的测试目标,比较直接的测试目标是
● 测试覆盖率,一般需要达到95%以上是一个比较好的状态,包括
○ 行覆盖率
○ 分支覆盖率
● 功能覆盖,有时候覆盖率达到了,不过很多不同使用场景仍然需要测试,比方说各种props是否正常,可以结合组件的demo,设计文件去设计测试用例

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/12726.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python编程 列表的常用方法

作者简介&#xff1a;一名在校计算机学生、每天分享Python的学习经验、和学习笔记。 座右铭&#xff1a;低头赶路&#xff0c;敬事如仪 个人主页&#xff1a;网络豆的主页​​​​​​ 目录 前言 一.字符串(str) 1.字符串常见操作(熟悉) 2.L.append(object) 3.L.exten…

前端开发环境搭建

1.安装git 去官网下载git安装包https://gitforwindows.org/ 2.配置ssh公钥这样再操作git 的时候就不用输入密码了 1、鼠标右键&#xff0c;点击 ‘git bash here’ 2、配置一下name 、eamil git config --global user.name 用户名 git config --global user.email 邮箱 3…

Spring 6面向切面编程aop详解

面向切面编程十五、面向切面编程AOP15.1 AOP介绍15.2 AOP的七大术语15.3 切点表达式15.4 使用Spring的AOP15.4.1 准备工作15.4.2 基于AspectJ的AOP注解式开发实现步骤通知类型切面的先后顺序优化使用切点表达式全注解式开发AOP15.4.3 基于XML配置方式的AOP&#xff08;了解&…

Windows 10下部署Java环境——jdk1.8.0_301版本

文章目录JDK安装JDK的环境配置验证JDK配置成功JDK安装 JDK1.8.0_301版本资源&#xff1a;https://download.csdn.net/download/qq_43408367/87061076?spm1001.2014.3001.5503 安装步骤参考&#xff1a;Windows 10下部署Java环境——JDK 11.0.2版本 JDK的环境配置 “我的电…

配置静态ip,主机名,centos安装jdk,hadoop等

切换到root用户操作 su root配置ip地址和ip地址的配置&#xff0c;网关&#xff0c;域名解析 vim /etc/sysconfig/network-scripts/ifcfg-ens33linux⾥的bootproto的none,static,dhcp有什么区别这个是⽹络配置参数&#xff1a; BOOTPROTOstatic 静态IP BOOTPROTOdhcp 动态IP…

消息队列 RocketMQ 5.0:从消息服务到云原生事件流平台

前言 回顾 RocketMQ 的发展历程&#xff0c;至今已十年有余。2022 年 RocketMQ 5.0 正式发布&#xff0c;全面迈进云原生时代。 11 月 5 日&#xff0c;2022 杭州 云栖大会上&#xff0c;阿里云智能高级产品专家杨秋弟在云原生峰会上发表主题演讲&#xff0c;发布消息队列 R…

【蓝桥杯冲击国赛计划第6天】字典

文章目录1. 字典1.1 概念1.2 字典的定义1.3 字典的添加1.4 字典的修改1.5 字典的删除1.6 字典的访问1.7 字典的排序2. 实例「弗里的语言」题目描述输入描述输出描述输入输出样例示例1示例2运行限制2.1 简单分析2.2 初始化2.3 get 访问2.4 补充2.5 完整代码3. 实例「快递分拣」题…

关于编辑器QScintilla(Scintilla)词法分析器取消非活动代码灰色显示

入门&#xff0c;首先看我这两篇博客&#xff1a;关于QScintilla库的入门大全https://biao2488890051.blog.csdn.net/article/details/126798996?spm1001.2014.3001.5502 关于编辑器QScintilla&#xff08;Scintilla&#xff09;词法分析器工作原理的分析&#xff08;实现注释…

MATLAB算法实战应用案例精讲-【智能优化算法】黏菌算法(SMA)(附MATLAB实现代码)

前言 黏菌觅食算法(Slime Mould Algorithm,SMA)由Li及Mirjalili教授等人于2020年提出,主要模拟了黏菌的扩散及觅食行为,利用自适应权重模拟了基于生物振荡器的“黏菌传播波”产生正反馈和负反馈的过程,形成具有良好的探索能力和开发倾向的食物最优连接路径,因此具有较好的…

JavaSE之反射

目录Class类反射的概念反射的好处反射的应用场景三种获取Class对象的方式Class类中的方法Class中获取构造器的方法获取成员方法对象获取成员变量反射案例最后Class类 Class类创建的对象我们称为Class对象/类对象/字节码对象 Class对象会保存类中的信息(构造方法, 成员方法, 成…

STM32CubeMX新建工程并点亮一个LED

可提前看&#xff1a;STM32CubeMX环境安装&#xff08;保姆级&#xff09; 目录 进入STM32CubeMX界面 双击软件&#xff0c;可能会出现的弹窗 更改固件路径 新建工程 进入配置环境 以一个点灯程序为例 GPIO配置 输出电平设置 输出模式 上下拉 输出速度 ​编辑 U…

【笑小枫的SpringBoot系列】【十六】SpringBoot生成PDF

关于笑小枫&#x1f495; Hello&#xff0c;我是笑小枫&#xff0c;欢迎来到我的世界&#xff0c;喜欢的朋友关注一下我呦&#xff0c;大伙的支持&#xff0c;就是我坚持写下去的动力。 笑小枫个人博客&#xff1a;https://www.xiaoxiaofeng.com 本文源码&#xff1a;https://g…

DVWA之SQL注入(盲注)

文章目录方法一&#xff1a;基于布尔盲注1.判断是否存在注入&#xff0c;注入类型2.猜数据库名3.猜解数据库中的表名4.猜列名5.猜表中的字段值方法二&#xff1a;基于时间盲注1.判断是否存在注入&#xff0c;注入是字符型还是数字型2.猜解当前数据库名3.猜解数据库中的表名4.猜…

ASPNetZero 11.4 Release Angular + MVC + Crack

Asp. NET 带有现代UI和稳定的结构&#xff0c;是新的网页应用的起点。它会提供预建和工作页以及很强的基本构造。 基础解决方案 你的下一个网络应用程序 ASP.NET Zero 是具有现代 UI 和 SOLID 架构以及完整源代码的新 Web 应用程序的起点。 ASP.NET Zero的好处 ASP.NET Zero 通…

化合物应用-动物给药方式

给药方式 针对实验动物的给药方式有很多种&#xff0c;总体可分为局部给药和系统给药。系统给药又可分为肠外给药和肠内给药。 1.1 肠内给药&#xff08;Enteral administration&#xff09; 口服&#xff08;per os&#xff0c;p.o&#xff09;给药由于具有经济、方便、安全…

虚拟形象sdk哪个好?可以快速制作专属元宇宙形象

元宇宙的火爆&#xff0c;催生了很多提供元宇宙基座的服务商&#xff0c;目前市面上提供虚拟形象SDK的服务商不少于20家&#xff0c;这里小编给大家推荐ZEGO即构科技的Avatar虚拟形象SDK。 即构Avatar虚拟形象SDK概述 即构Avatar支持自定义管理人物的虚拟形象&#xff0c;通过默…

【虹科新品】 HK-MR430330绝对式光纤编码器(上)

虹科MR430&330系列绝对式光纤编码器是用于确定轴位置的角度计&#xff0c;均为纯光学无源设计。HK-MR330系列适用于间隔距离较大的编码器和控制器&#xff0c;HK-MR430 系列适用于由于体积小而提供小空间的系统。与增量式旋转编码器相比&#xff0c;测量值在开启后立即可用…

【JAVA程序设计】基于SSM的图书管理系统-有论文文档

基于SSM的图书管理系统-有文档项目获取项目简介开发环境项目技术功能结构文档目录运行截图项目获取 获取方式&#xff08;点击下载&#xff09;&#xff1a;是云猿实战 项目经过多人测试运行&#xff0c;可以确保100%成功运行。 项目简介 这是一个基于ssm的图书管理系统&…

canvas绘制时钟

这篇文章用于介绍html5的新标签&#xff0c;<canvas></canvas> Canvas介绍 canvas是HTML5新增的元素&#xff0c;通过javascript脚本绘制图形。那么canvas可以用来干啥呢&#xff1f; 制作web网页游戏数据可视化&#xff1b;即&#xff1a;echarts就是基于canva…

ajax 学习记录

ajax 学习记录ecplise 下载安装创建项目创建ajax后台请求处理类新建包创建servletAjaxServlet 内容web.xml新建index.htmlindex.html内容运行ecplise 下载安装 ecplise 21.06 Eclipse IDE for Enterprise Java and Web Developers 下载 下载完直接解压运行 创建项目 先下载…