react-testing-library初体验

564 阅读4分钟

简介:react-testing-library 是一个用于测试 React 组件的库,其方式类似于最终用户使用组件的方式。 它非常适合 React 组件和应用程序的单元测试、集成和 e2e 测试。它更直接地与 DOM 节点一起使用,因此建议与 jest-dom 一起使用,这样可以方便的做断言。

why? 那为什么要写单元测试,公司不是有测试吗?我们写单元测试也是为了后期代码能够减少bug的发生,当出现bug我们能快速定位到bug所在的位置,而我的话是给自己的组件库写上一些简单的测试。

测试要深入研究的话估计得花个很久的时间,而我的话就是使用的其中一个功能——断言,相信很多小伙伴对这个断言并不陌生,就是预期达到某种效果,我们在最开始学习js的时候就已经接触过了,类似于:

// 我们写个很简单例子
// 这个是测试的函数
let ensure = function (condition, message) {
    if (!condition) {
        console.log(message)
    }
}
// 这个是需要测试的函数,两数相加
const addNumber = (a, b) => {
    let sum = a + b
    return sum
}
// 测试方法
let testAddFn = (num1 = 10, num2 = 20, sum = 10) => {
    // 如果测试没问题那么就不会输出,否则会输出 addNumber错误
    ensure(sum == addNumber(num1, num2), 'addNumber 错误')
}
testAddFn()

addNumber(10, 20) // 我们知道等于30,如果不等于30那么就是代码出错了

我们可以看到上述测试的代码是一个函数,他的作用是处理两个数相加,假如我想测试它的功能时,那么我们就可以像我一样添加测试,我期望它得到某种效果

测试的功能没问题的时候就不会做任何输出

image.png

测试的功能出现问题的时候就会输出预期的错误

image.png

好了,那么我们已经知道测试是什么了,那么现在开始了解断言,这里我就介绍几个常用的api,以及用法

api

  1. describe描述, decribe会形成一个作用域,我们可以把需要测试的某一块功能或者某个组件写在这个作用域下面防止测试多的话造成混乱
  2. it断言,方便我们在里面写断言, 一个描述里面可以有多个断言
  3. expect期望,期望达到某种效果
  4. test 测试,类似it
  5. render 方法返回一个包裹对象, 对象包裹一些对DOM的查询和获取方法
  6. getByText通过 存在的text 来定位元素
  7. toBeInTheDocument 断言它要出现在页面中
  8. toEqual比较他们的值是否相等
  9. toBeFalsy断言它不存在或者是false
  10. toHaveBeenCalled断言某个事件是否存在
  11. fireEvent模拟用户操作的一个功能
  12. toHaveClass断言有哪些类名
  13. toContainHTML断言当前渲染的元素里面包含哪些元素
  14. toHaveAttribute 断言给定元素是否具有属性

我就拿一个Button组件来做例子, 先附上源码

import React from 'react';
import classnames from 'classnames';
import './index.less';

export type ButtonType = 'default' | 'dashed' | 'primary' | 'danger';
export type ButtonSize = 'small' | 'medium' | 'large';
export type ButtonHtmlType = 'button' | 'submit' | 'reset';

export interface ButtonProps {
  children?: React.ReactNode;
  type?: ButtonType;
  disabled?: boolean;
  icon?: React.ReactNode;
  size?: ButtonSize;
  ghost?: boolean;
  htmlType?: ButtonHtmlType;
  loading?: boolean;
  onClick?: React.MouseEventHandler;
  onMouseEnter?: React.MouseEventHandler;
  onMouseLeave?: React.MouseEventHandler;
  onFocus?: React.FocusEventHandler;
  onBlur?: React.FocusEventHandler;
  className?: string;
  style?: React.CSSProperties;
}

const Button: React.FC<ButtonProps> = (props) => {
  const {
    icon,
    loading,
    size,
    type,
    htmlType,
    ghost,
    style,
    className,
    disabled,
    children,
    onClick,
    ...rest
  } = props;

  const handleClick = (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
    if (disabled) {
      return;
    }
    if (onClick) {
      onClick(event);
    }
  };

  const classes = classnames({
    'hs-button': true,
    'hs-button-default': type === 'default',
    'hs-button-primary': type === 'primary',
    'hs-button-dashed': type === 'dashed',
    'hs-button-danger': type === 'danger',
    'hs-button-small': size === 'small',
    'hs-button-medium': size === 'medium',
    'hs-button-large': size === 'large',
    'hs-button-ghost': ghost,
    'hs-button-disabled': disabled,
  });

  return (
    <button
      className={classes}
      style={style}
      type={htmlType}
      disabled={disabled}
      onClick={handleClick}
      {...rest}
    >
      {props.icon && icon}
      {children}
    </button>
  );
};

Button.defaultProps = {
  type: 'default',
  size: 'medium',
  disabled: false,
  htmlType: 'button',
};

export default Button;

接下来是编写单元测试, 首先引入需要测试的组件, 以及@testing-library/react和@testing-library/jest-dom这两个库

import React from 'react';
import { render, fireEvent } from '@testing-library/react';
import '@testing-library/jest-dom';
import Button, { ButtonProps } from '../Button';

const defaultProps: ButtonProps = {
  disabled: false,
  onClick: jest.fn(),
};

describe('描述:测试Button组件', () => {
  it('添加断言:应该渲染正确的默认Button组件', () => {
    // render 方法返回一个包裹对象, 对象包裹一些对DOM的查询和获取方法
    const wrapper = render(<Button {...defaultProps}>hs</Button>);
    const element = wrapper.getByText('hs') as HTMLButtonElement;
    expect(element).toBeInTheDocument();
    // 断言他是一个button组件
    expect(element.tagName).toEqual('BUTTON');
    // 元素上是否存在disabled禁用属性
    expect(element.disabled).toBeFalsy();
    // fireEvent触发事件
    fireEvent.click(element);
    expect(defaultProps.onClick).toHaveBeenCalled();
  })
})

写好之后我们就可以开始测试代码了, 使用命令yarn run test 或者 npm run test

image.png

以上可以看到我们的代码没出现任何问题, 绿色的勾表示测试通过, 当我们修改了它的源代码导致有些功能和预期的代码不符合, 那么测试就不会被通过了, 你学会了吗?