初尝 Jest 单元测试

最近的几次发布都犯了小错,都是缺乏或者忽视了测试所导致的。通常来说,一个新功能上线的时候,开发和测试都投入比较多,各项测试都是比较全面的。然而,发布上线也并非意味着不再有bug或者修改。那这时候问题来了,有些修改, 我们会以为很简单,从而放松警惕,偷懒也罢,没有精力也罢,简单验证之后便匆匆发布了。此时,有可能不经意的改动对其它功能造成了影响,bug复bug, bug何其多呀。

那web页面的发布,其优势是可以快速上线新功能或者bugfix,节奏很快,而其缺点也明显,相对于终端的版本发布需要重新走一遍比较重的测试流程而言,就没那么谨慎了。

那web也引入自动化测试吧

当然了,自动化测试不是说一句话那么简单了,前期选型框架,编写用例测试团队都不一定能支持得上,而且web功能变化如此频繁,更新用例说不定还真不如手工过一遍。

挑点简单可动手的, 开发同学自己写单元测试吧。

问题也就来了,做业务需求都没时间了,还要写测试用例?哪来的时间。。。

所以,写单元测试这件痛苦的事情,怎么办?

不同于几年前js乱七八糟,模块化都不普遍的时代,目前团队里主流技术栈就是React,以React天生强制组件化的思想来看,写单元测试应该是天时地利了,而Facebook也提供了配套的测试工具(ReactTestUtils)和测试框架(Jest),所以,看怎么样在已有项目快速补充上单元测试吧。

Jest的口号是 Delightful JavaScript Testing,真的吗?

直奔相关主题,Jest 官网有一个tab Testing React Apps, 那对React是有特别照顾呀。

Snapshot Testing

所谓snapshot,即快照也。通常涉及UI的自动化测试,思路是把某一时刻的标准状态拍个快照,在测试回归的时候进行pixel to pixel的对比。但Jest对React组件的快照则不同,其实是把一个组件给序列化成纯文本, 纯文本的比较,这个真是简单又高效呀。对于一个React组件而言, 传入相同的props,我们是期望得到相同的输出, 这样子一来,通过构造不同的props, 我们即有了不同的测试用例。理想状态中,组件若是无内部状态变化,测试用例覆盖率应该可以达到100%了。当然,仅仅是理想。

先跑跑官网的简单例子,先照步骤安装npm依赖,然后敲代码,jest跑一下:

// Link.react.js
import React from 'react';

const STATUS = {
  HOVERED: 'hovered',
  NORMAL: 'normal',
};

export default class Link extends React.Component {

  constructor(props) {
    super(props);

    this._onMouseEnter = this._onMouseEnter.bind(this);
    this._onMouseLeave = this._onMouseLeave.bind(this);

    this.state = {
      class: STATUS.NORMAL,
    };
  }

  _onMouseEnter() {
    this.setState({class: STATUS.HOVERED});
  }

  _onMouseLeave() {
    this.setState({class: STATUS.NORMAL});
  }

  render() {
    return (
      <a
        className={this.state.class}
        href={this.props.page || '#'}
        onMouseEnter={this._onMouseEnter}
        onMouseLeave={this._onMouseLeave}>
        {this.props.children}
      </a>
    );
  }
// Link.react-test.js
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();
});

第一次跑的时候,就会生成一个快照文件,在__snapshots__目录下:

exports[`renders correctly 1`] = `
<a
  className="normal"
  href="http://www.facebook.com"
  onMouseEnter={[Function]}
  onMouseLeave={[Function]}
>
  Facebook
</a>
`;

在之后的toMatchSnapshot()调用就会与之比较,如有不同,则是用例失败,会打印出具体差异:

如果是代码有修改,需要对应更新快速的话,则执行jest -u重新生成。

例子简单了, 怎么引入现有的项目呢? 从其需要的依赖来看,

npm install --save-dev jest babel-jest babel-preset-es2015 babel-preset-react react-test-renderer

它是直接支持jsx语法和es6语法的,跑了一个最简单的组件,it works!

再跑一个,发现失败了,报找不到文件。观察下出错信息,发现是有一些文件引用是依赖构建工具处理的。比如说import util from assets/util jest运行的时候只在 node_modules 下去,当然找不到了。

机智的facebook团队早就想到了,Using with webpack 虽然项目用的是fis构建,但是思路是可以参考的,就是给jest加个解析路径的配置,在package.json中添加jest项配置,或者通过--config 参数指定配置文件:

"jest": {
    "collectCoverage": true,
    "testMatch": [ "**/__tests__/**/*.test.js?(x)"],
    "moduleDirectories": ["node_modules", "src"], // 就是这个
    "testPathIgnorePatterns": [
      "/node_modules/",
      "/dev",
      "/src/server"
    ]
  }

在仅仅使用toMatchSnapshot()的情况下,

分支测试覆盖率达到80%以上了,而有一些代码还没有覆盖到,其实是因为组件内部有交互行为,比如说onClick,再继续下补充之后:

我们发现,整体覆盖率都大体提升,而实际上,仅仅就是加了20几行代码而已,就是处理onClick事件,图片的onError事件。

看起来,这样子添加测试用例,倒也不是很麻烦的样子,主要是snapshots的使用,有四两拨千斤效,不过重点在于其输入数据的构造。

可期。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏SEO

SEO常见疑问整理总结(一)

2917
来自专栏极乐技术社区

我的第一个小程序(Discuz! + 微信小程序)

Github : https://github.com/DowneyL/di...‘

4327
来自专栏闰土大叔

在没有DOM操作的日子里,我是怎么熬过来的(中)

前言 继上篇推送之后,在掘金、segmentfault、简书、博客园等平台上迅速收到了不俗的反馈,大部分网友都留言说感同身受,还有不少网友追问中篇何时更新。于是...

33311
来自专栏木制robot技术杂谈

利用爬虫和树莓派3打造自己的语音天气闹钟

前言 前不久又一次一个人在他乡过了生日,悄悄买了一台树莓派3送给自己做生日礼物。终于算是实现了大学以来一直的一个小愿望。买回来之后当然不能让他落灰,于是就利用自...

83010
来自专栏逍遥剑客的游戏开发

从Native到Web(五), emscripten学习笔记: 初体验

1373
来自专栏张戈的专栏

另类SEO分享:利用JS封装iframe躲过搜索引擎的抓取

前言:很多博友不仔细看完内容就直接认为用 iframe 不好之类的云云,而实际上本文就是教你在必须使用 iframe 的时候,该如何躲过搜索引擎的抓取,避免不利...

4226
来自专栏静晴轩

Sublime Text 最新注册码分享

SublimeText,她作为强大而小巧,性感且快捷的编辑器,长时间以来,受众人青睐;对于个人来说,也是最常用代码编辑器,且没有之一(目前同时也会用 VsCod...

3463
来自专栏pangguoming

Android平台GPS系统的应用开发

第一部分、前述: Android作为Google移动互联网战略的重要组成部分,将进一步推进“随时随地为每个人提供信息”这一企业目标的实现。Google的目标是让...

4704
来自专栏iOSDevLog

Unity 3D 开发《王者荣耀》:英雄攻击创建按钮源码:https://github.com/iOSDevLog/ArenaOfValor

2296
来自专栏Bug生活2048

几行代码搞定识别图片中的文字信息,同时转换成语音

实现的方式还是挺多的,这里介绍下百度的AI开放平台,毕竟大公司,感觉识别的精度会高点,同时相信他们的算法也会不断优化,我等小菜鸟只要会用就可以啦。

981

扫码关注云+社区