图片来自 https://blog.algolia.com 在上一篇文章中我们介绍了什么是单元测试以及单元测试框架 Jest 的基本用法。在本文中我们会具体聊聊如何用 Jest 为 React 组件编写单元测试。
首先我们要明确的一点,那就是 React 组件的单元测试本质是也是单元测试。因此它也符合我们之前介绍过的单元测试的全部特点。唯一不同的地方在于 React 组件的单元测试中我们需要找到合适的方法对执行结果进行断言。换言之,我们要根据 React 的特点来设置代码是否正确执行的判断条件。
那么 React 组件和其它的被测试对象有何不同呢?仔细想过,我们会发现:
React 组件的 render 结果是一个组件树,并且整个树最终会被解析成一个纯粹由 HTML 元素构成的树形结构 React 组件可以拥有 state,且 state 的变化会影响 render 结果 React 组件可以拥有生命周期函数,这些生命周期函数会在特定时间点执行 知道了要测试的内容,接下来的问题就是如何执行一个 React 组件并编写断言了。如何执行一个 React 组件呢?看到这个问题估计大多数儿人是蒙的。平时不就是直接 ReactDOM.render 吗?不错,ReactDOM.render 确实可以执行一个 React 组件并将它渲染到页面中,但这种方式不利于编写测试代码。
有没有更简单的方式呢?其实 React 已经帮我们提供好了工具,让我们一起来看看。
React 提供的测试工具 在 React 的官方文档中提到了两个用于测试 React 组件的库。让我们分别介绍。
react-test-renderer 在说 react-test-renderer 之前,让我们先聊聊什么是 renderer。React 最早是被用来开发网页的,所以早期的 React 库中还包含了大量和 DOM 相关的逻辑。后来 React 的设计思想慢慢被迁移到其它场景,最被人们熟知的莫过于 React Native 了。为了灵活性和扩展性,React 的代码被分拆为 React 核心代码与各种 renderer。React 自带了 3 个 renderer,前两个是大家常见的:
什么是单元测试? 测试是一种验证我们的代码是否可以按预期工作的手段。
被测试的对象可以是我们程序的任何一个组成部分。大到一个分为多步骤的下单流程,小到代码中的一个函数。
单元测试特指被测试对象为程序中最小组成单元的测试。这里的最小组成单元可以是一个函数、一个类等等。
单元测试的优势 由于被测试对象的简单(通常只有一个或多个输入以及一个输出),这就决定了单元测试开发起来也很简单,通常每个测试只有几行到十几行不等。测试代码的简单表示它可以被更频繁的执行(事实上,很多单元测试框架都有 watch 模式。每次改动代码时都会自动执行单元测试)。更频繁的执行意味着更早的发现问题。
试想,随着代码的不断迭代,程序中总有某些位置会频繁出现某类问题。在没有单元测试时程序员之间往往都是“口口相传”,隔一段时间很可能由于疏忽还会犯同一个错误。有了单元测试我们就可以为这些问题点编写对应的测试代码,每次提交代码前都执行一遍,可以极大的降低相同 bug 重复出现的概率。
此外,要为一个被测试对象编写单元测试,那么它应该首先是容易被测试的(这似乎是一句废话)。反过来讲,如果你面对一个函数、类却很难编写测试代码的时候,很可能是你的代码设计上存在问题。比如和外部依赖耦合过于紧密。这种情况下,编写单元测试的过程会倒逼我们优化我们代码的结构。将复杂的代码拆解成为更简单、更容易测试的片段。这个过程本身也会潜移默化的提高我们代码的质量。
单元测试的限制/不足 I get paid for code that works, not for tests - Kent Beck
首先,测试代码再简单,也是需要工作量来开发的。必定占用开发人员的时间。因此需要开发人员在投入与收益之间找到一个最佳的平衡点。
其次,单元测试覆盖率往往会给开发人员一种错觉:这段代码的单元测试都通过了(测试覆盖率以及 100% 了),肯定没有 bug。其实不然,单元测试覆盖率与代码质量没有必然的联系。作为开发人员必须尽早认识到这一点。
何时编写单元测试? 开发过程中,单元测试应该来测试那些可能会出错的地方,或是那些边界情况。 维护过程中,单元测试应该围绕着 bug 进行,每个 bug 都应该编写响应的单元测试。从而保证同一个 bug 不会出现第二次。 单元测试中的基本概念? 单元测试一般包含以下几个部分:
被测试的对象是什么 要测试该对象的什么功能 实际得到的结果 期望的结果 mock / spy (下文会详述) 具体到某个单元测试,往往包含以下几个步骤:
准备阶段:构造参数,创建 spy 等 执行阶段:用构造好的参数执行被测试代码 断言阶段:用实际得到的结果与期望的结果比较,以判断该测试是否正常 清理阶段:清理准备阶段对外部环境的影响,移除在准备阶段创建的 spy 等 Jest 简介 Jest 是 Facebook 开发的一款 JavaScript 测试框架。在 Facebook 内部广泛用来测试各种 JavaScript 代码。其官网上主要列出了以下几个特点:
最近和小伙伴们在尝试做把项目中经常用到的 React 组件整理成一套 React 组件库。为了保证代码质量且方便日后维护,我们决定为组件编写单元测试。接下来几篇文章会聊聊在这个过程中我们遇到的问题以及一些思考。
正文开始 要写单元测试,首先面临的一个问题就是“我该使用哪个测试框架”?
如果你去 Google 上搜索“how to test react component”,在结果页面中你大概会来来回回看到以下几个名词/概念:
Jest Enzyme Mocha全家桶(套餐内容大概率是:Mocha + Chai + Sinon + Istanbul) react-test-renderer react-dom/test-utils 首先让我们来看看这些库的功能和定位,然后再来做选择。
🃏 Jest Jest 是 Facebook 开发的一款 JavaScript 测试框架。在 Facebook 内部广泛用来测试各种 JavaScript 代码。其官网上主要列出了以下几个特点:
轻松上手 使用 create-react-app 或是 react-native init 创建的项目已经默认集成了 Jest 现有项目,只需创建一个名为 __test__ 的目录,然后在该目录中创建以 .spec.js 或 .test.js 结尾的文件即可 内置强大的断言与 mock 功能 内置测试覆盖率统计功能 内置 Snapshot 机制 虽然 Jest 官网介绍中多次 React,但实际上 Jest 并不是和 React 绑定的。你可以使用它测试任何 JavaScript 项目。
Travis是一个众所周知的持续集成服务提供商,免费为开源项目提供持续集成服务。但是对于闭源项目/private repo需要购买travis pro服务才能使用。对于小团队,每月129刀的最低消费实在是一笔不小的开支,因此便有了本文。
为什么要做这件事? 首先,我们是一群有情(jie)节(cao)的程序猿。我们要对自己的代码负责,保证所有上线的代码都是经过测试的。所有Pull Request的code要经过team member的revew并跑通测试。
其次,我们是一群节(diao)俭(si)的程序猿。Travis Pro每个月129刀的最低消费让我们望(xia)而(niao)却(le)步(!)。 要搭建这样一台持续集成服务器,需要哪些东西? 一台linux主机 (本文中使用的是linode上的Ubuntu 12.04) 一个GitHub账号 (加为你repo的collaborator或是team的member) 如何配置? Step1 安装Jenkins,非Ubuntu系统安装Jenkins请参考Jenkins官方文档
wget -q -O - http://pkg.jenkins-ci.org/debian/jenkins-ci.org.key | sudo apt-key add - sudo echo 'deb http://pkg.jenkins-ci.org/debian binary/' >> /etc/apt/sources.list sudo apt-get update sudo apt-get install jenkins Setp2 为jenkins用户安装rvm
sudo su - jenkins curl -L https://get.rvm.io | bash -s stable --ruby=1.9.3 Setp3 安装Xvfb。(如果你的测试不需要用模拟的DISPLAY,那你可以忽略此步骤)
我们测试Rails时需要用capybar跑feature spec。有js相关的测试需要启动一个浏览器来执行测试代码,但是linode上的Ubuntu都是没有display的,因此跑feature spec的时候会遇到无法启动浏览器而报错的情况。Xvfb就是用来创建一个虚拟的Display供启动浏览器用的。
sudo apt-get install xvfb Xvfb :99 -screen 0 1024x768x16 >> /tmp/Xvfb.