最近想给本人的 ui 轮子写一个单元测试, 为什么会这么想呢? 因为它能涨工资呀, 哈哈哈, 当然咱们学习是不能那么功利的(放 P, 哈哈哈)
除了涨工资没别的了? 当然不是, 他是有它的益处的, 否则学他干嘛
单元测试的益处是啥?
- 重构、重构、重构,重要的事件说三遍
TDD(测试驱动开发)的具体实现就是通过红灯 -> 绿灯 -> 重构一直反复,一步一步去强壮咱们的代码,保障今后重构代码的时候测试的精确,能够在重构中精确的定位到问题。同时也为当前的开发提供反对,在测试的根底上咱们能够重构构造和业务性能。 - 单元测试是最好的正文
测试会提醒你哪些步骤是能够通过、如何应用的最好文档。更具体的标准了测试指标的边界值与非法值。 - 定位 bug,缩小 bug
单元测试能够通过不同的条件来发现问题在哪里,在一些弱类型的语言中也防止了一些类型查看的低级谬误,当然这个当初咱们都用 TypeScript 做到了。 - 被迫的标准组织构造
可能平时咱们会把一个办法写的很简单、一个类写的很大,没有想过如何去组织构造,但如果你想到你行将的测试要如何写的时候,那可能你在开发前必须要想想哪些局部能够提出来了。这样会缓缓养成很好的思维。
好了, 不多 BB, 看看怎么用吧!!!
我用的是 jest 测试哦!!!
首先开始看一下我之前的配置, 之前的文章有我的配置, 防止你找的烦, 我把配置放在这里 https://github.com/sunkuangdo… 须要的请自行下载吧!
1. 看一下我的 jest.config.js 中 testMatch
, 通知我须要在 lib 文件夹中创立个目录__tests__
,__tests__
的目录外面 xxxx.unit.(js|jsx|ts|tsx)
这样的文件就是测试文件
那就创立个文件夹和文件呗
2. 我要测试啥呢? 我先测试我 ui 轮子外面的一个函数, 试一下
这是我之前写的一个函数, 就测试他, 他会承受一个 className
3. 在 classes.unit.jsx 外面写一个, 单元测试
// 先将测试文件导入进来
import classes from "../helpers/classes";
// 要在 describe 外面进行测试
describe('classes', () => {
// it 是申明, 第一个参数是对测试的形容, 第二个参数是函数, 外面是对测试的执行
it('他承受 className', () => {
// 我给 classes 函数传了一个字符串 a
const result = classes('a')
// 我断言 result 会是一个字符串 a
expect(result).toEqual('a')
})
})
4. 哈哈哈, 乐不可支的运行一下, 如果你是 yarn 并且用的我得配置, 就用 yarn test
MMP, 报错了, 说我少了一个配置 ’babel-preset-react-app’, 装置一下呗
yarn add --dev babel-preset-react-app
5. 装置胜利了, 再来一下看看yarn test
, 哎, 胜利, 真香!
6. 每次改写测试文件, 都要我yarn test
, 好烦 …., 试试这个操作yarn test --watch
, 每次更改都会自动测试啦!
7. 我要是想测试一个 icon 组件怎么办? 来, 咱们持续搞起来
看上面这段代码
import * as renderer from 'react-test-renderer'
import React from "react";
import Icon from '../icon'
describe("icon",()=>{it("ICON 是 SVG",()=>{
// 首先用 renderer 创立一个 Icon, 而后把 Icon 转成 json
const json = renderer.create(<Icon/>).toJSON();
// 这个 json 进行快照
expect(json).toMatchSnapshot()})
})
8. 当我运行到这里认为能够进行测试的时候, 意外呈现了 ….bug 了, 起因是我测试的 Icon 组件中有用到 scss 没有配置, 无奈辨认
首先在 test 目录下创立新的目录, 并创立上面的两个文件
而后在 file-mock.js 中退出这段代码 module.exports = 'test-file-stub';
在 object-mock.js 中退出这段代码 module.exports = {};
最外层找到 jest.config.js 文件, 替换这段
moduleNameMapper: {"\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/test/__mocks__/file-mock.js",
"\\.(css|less|sass|scss)$": "<rootDir>/test/__mocks__/object-mock.js",
},
下面这段代码这里来解释一下:
- 测试时候, 所有的 jpg|jpeg| 等等用
/test/__mocks__/file-mock.js
替换掉 - 测试时候, 所有的 (css|less|sass|scss) 用
/test/__mocks__/object-mock.js
替换掉
9. 哈哈哈, 再跑一下, 嗯 ….., 本认为解决了, 唉!!! 新问题呈现了, 来源于这个文件的报错
嗯? 我测试的时候也不须要测试 svg 啊, 果决的删除console.log(error)
, 这样子就不报错了
10. 咱们来看一下这段代码expect(json).toMatchSnapshot()
, 他做了什么样的事件, 咱们看一下.snap
<svg
className="fui-icon"
>
<use
xlinkHref="#undefined"
/>
</svg>
他会帮咱们生成一个 svg, 为什么 use 会是 undefined??? 因为我还没有给他传 name 值, 给 name 传递一个值, 从新运行 yarn test 之后, 报错了
他说咱们批改了内容, 然而快照没有批改, 与快照不同, 这时候须要从新运行, 批改快照外面的内容, 从新运行 yarn test -u
意味着保留咱们最新的正确的快照
11. 咱们来测试一下 onClick, 首先下载两个库 enzyme
和enzyme-adapter-react-16
, 运行
yarn add --dev enzyme
yarn add --dev enzyme-adapter-react-16
配置 test 目录下的 setupTests.js 文件
const enzyme = require('enzyme')
const Adapter = require('enzyme-adapter-react-16')
enzyme.configure({adapter: new Adapter()})
接下来咱们 onClick 进行测试, 一点一点看他表白了什么意思
咱们是要测试一个点击事件函数, 导入了这个库之后, 这个库帮咱们生成了一个假的页面, 并将测试的函数传入 Icon 组件, 找到 svg 模仿点击 svg, 咱们本人创立一个 fn 测试看看对不对, 咱们期待 n 等于 2, 如果不确定是否测试胜利, 将 n 改成 1 试一试
import {mount} from 'enzyme'
describe("icon", () => {it("onClick", () => {
let n = 1;
const fn = () => {n = 2}
// 生成假想页面
const comonent = mount(<Icon name="baidu" onClick={fn}/>)
// 找到 svg 模仿点击 svg
comonent.find('svg').simulate('click')
// 咱们期待 n 等于 2
expect(n).toEqual(2)
})
})
然而咱们这个办法有点笨, 每次测试都要本人写个 fn, 傻乎乎的!!!
能不能让 jest 帮咱们写一个 fn 呢 const fn = jest.fn();
这段代码是让 jest 创立一个 fn()expect(fn).toBeCalled();
咱们期待 fn 会被调用
it("onClick", () => {const fn = jest.fn();
const component = mount(<Icon name="baidu" onClick={fn}/>)
component.find('svg').simulate('click')
expect(fn).toBeCalled();})
运行 yarn test, 测试通过了, 然而咱们不晓得是不是真的进行测试了, 创立一个 fn2, 这个 fn2 咱们并不会传, 看看报错了没有, 报错了, 就是真的测试了, 阐明 onClick 被调用了, 测试通过了
总结
- 这篇文章是自己在写 Icon 组件中, 学习 jest 单元测试进行的配置, 以及语法
- 咱们理解到了, 单元测试的益处, 为什么要学他 ….
- 咱们学习到了配置, 以及语法示意的含意
这便是自己在学习过程中和大家的分享, 一遍遍踩坑, 一遍遍爬过去会有许多成长, 置信大家也肯定有这种感触, 一起致力吧!!!