React单元测试笔记

什么是单元测试

单元测试(unit testing),是指对软件中的最小可测试单元进行检查和验证。

React常用的单元测试工具

Jest+Enzyme

Jest使用

安装

用yarn安装jest:
yarn add --dev jest

或者npm:

npm install --save-dev jest

使用Babel

通过yarn安装依赖:

1
yarn add --dev babel-jest @babel/core @babel/preset-env

babel.config.js文件里添加配置:

1
// babel.config.js
2
module.exports = {
3
  presets: [
4
    [
5
      '@babel/preset-env',
6
      {
7
        targets: {
8
          node: 'current',
9
        },
10
      },
11
    ],
12
  ],
13
};

例子

首先创建一个 sum.js文件:

1
function sum(a, b) {
2
  return a + b;
3
}
4
module.exports = sum;

然后创建 sum.test.js:

1
const sum = require('./sum');
2
3
test('adds 1 + 2 to equal 3', () => {
4
  expect(sum(1, 2)).toBe(3);
5
});

添加这部分代码到 package.json:

1
{
2
  "scripts": {
3
    "test": "jest"
4
  }
5
}

最后运行yarn test 或者 npm run test ,jest将会打印如下的输出:

1
PASS  ./sum.test.js
2
✓ adds 1 + 2 to equal 3 (5ms)

基本方法

  • 分组(Test Group):describe(name, fn)

    describe创建一个将几个相关测试组合在一起的块,这不是必须的,而且可以嵌套。

    1
    const myBeverage = {
    2
      delicious: true,
    3
      sour: false,
    4
    };
    5
    6
    describe('my beverage', () => {
    7
      test('is delicious', () => {
    8
        expect(myBeverage.delicious).toBeTruthy();
    9
      });
    10
    11
      test('is not sour', () => {
    12
        expect(myBeverage.sour).toBeFalsy();
    13
      });
    14
    });
  • 测试用例(Test Case):test(name, fn, timeout)

    test是jest.It的别名。

    test用来编写测试方法。

    1
    test('did not rain', () => {
    2
      expect(inchesOfRain()).toBe(0);
    3
    });
  • 断言(Assert):expect(value).toBe(value)

    对结果进行断言。

    expect判定条件

    toBe匹配器

    not判定相反的结果

    1
    expect(2 + 2).toBe(4);
    2
    expect(2 + 2).not.toBe(5);

匹配器(Matchers)

普通匹配器

最简单的测试值的方法是看是否精确匹配。

1
test('two plus two is four', () => {
2
  expect(2 + 2).toBe(4);
3
});

Truthiness

在测试中,你有时需要区分 undefinednull,和 false,但有时你又不需要区分。 Jest 让你明确你想要什么。

  • toBeNull 只匹配 null

  • toBeUndefined 只匹配 undefined

  • toBeDefinedtoBeUndefined 相反

  • toBeTruthy 匹配任何为真的 if 语句

  • toBeFalsy 匹配任何 为假的if 语句

1
test('null', () => {
2
  const n = null;
3
  expect(n).toBeNull();
4
  expect(n).toBeDefined();
5
  expect(n).not.toBeUndefined();
6
  expect(n).not.toBeTruthy();
7
  expect(n).toBeFalsy();
8
});
9
10
test('zero', () => {
11
  const z = 0;
12
  expect(z).not.toBeNull();
13
  expect(z).toBeDefined();
14
  expect(z).not.toBeUndefined();
15
  expect(z).not.toBeTruthy();
16
  expect(z).toBeFalsy();
17
});

数字

多种比较数值的匹配器。

1
test('two plus two', () => {
2
  const value = 2 + 2;
3
  expect(value).toBeGreaterThan(3);
4
  expect(value).toBeGreaterThanOrEqual(3.5);
5
  expect(value).toBeLessThan(5);
6
  expect(value).toBeLessThanOrEqual(4.5);
7
8
  // toBe and toEqual are equivalent for numbers
9
  expect(value).toBe(4);
10
  expect(value).toEqual(4);
11
});

比较浮点数相等时,使用toBeCloseTo而不是toEqual,可以避免舍入误差导致的测试错误。

1
test('两个浮点数字相加', () => {
2
  const value = 0.1 + 0.2;
3
  //expect(value).toBe(0.3);           这句会报错,因为浮点数有舍入误差
4
  expect(value).toBeCloseTo(0.3); // 这句可以运行
5
});

字符串

可以用 toMatch通过正则表达式检查字符串︰

1
test('there is no I in team', () => {
2
  expect('team').not.toMatch(/I/);
3
});
4
5
test('but there is a "stop" in Christoph', () => {
6
  expect('Christoph').toMatch(/stop/);
7
});

数组和迭代器

可以用toContain匹配是否包含此元素。

1
const shoppingList = [
2
  'diapers',
3
  'kleenex',
4
  'trash bags',
5
  'paper towels',
6
  'beer',
7
];
8
9
test('the shopping list has beer on it', () => {
10
  expect(shoppingList).toContain('beer');
11
  expect(new Set(shoppingList)).toContain('beer');
12
});

例外

如果你想要测试的特定函数抛出一个错误,在它调用时,使用 toThrow

1
function compileAndroidCode() {
2
  throw new ConfigError('you are using the wrong JDK');
3
}
4
5
test('compiling android goes as expected', () => {
6
  expect(compileAndroidCode).toThrow();
7
  expect(compileAndroidCode).toThrow(ConfigError);
8
9
  // You can also use the exact error message or a regexp
10
  expect(compileAndroidCode).toThrow('you are using the wrong JDK');
11
  expect(compileAndroidCode).toThrow(/JDK/);
12
});

更多

参考文档

测试 React Apps

使用Create React App安装

Create React App 初始化的项目预置了Jest,只需要添加react-test-renderer`来渲染快照.

不使用Create React App安装

如果现有项目不是通过Creact React App创建,则需要添加一些依赖来搭配使用

1
yarn add --dev jest babel-jest @babel/preset-env @babel/preset-react react-test-renderer

添加这些配置到相应的文件,current-version为当前依赖的最新版本号

1
// package.json
2
  "dependencies": {
3
    "react": "<current-version>",
4
    "react-dom": "<current-version>"
5
  },
6
  "devDependencies": {
7
    "@babel/preset-env": "<current-version>",
8
    "@babel/preset-react": "<current-version>",
9
    "babel-jest": "<current-version>",
10
    "jest": "<current-version>",
11
    "react-test-renderer": "<current-version>"
12
  },
13
  "scripts": {
14
    "test": "jest"
15
  }
16
// babel.config.js
17
module.exports = {
18
  presets: ['@babel/preset-env', '@babel/preset-react'],
19
};

搭配Enzyme使用

运行yarn add --dev enzyme添加Enzyme。如果您使用的是低于15.5.0的React版本,则还需要安装react-addons-test-utils

例子:

1
import React from 'react';
2
import {shallow} from 'enzyme';
3
import CheckboxWithLabel from '../CheckboxWithLabel';
4
5
test('CheckboxWithLabel changes the text after click', () => {
6
  // Render a checkbox with label in the document
7
  const checkbox = shallow(<CheckboxWithLabel labelOn="On" labelOff="Off" />);
8
  expect(checkbox.text()).toEqual('Off');
9
  checkbox.find('input').simulate('change');
10
  expect(checkbox.text()).toEqual('On');
11
});