测试你的前端代码 - part4(集成测试)

开发 开发工具
本文介绍集成测试(Integration Testing)。

测试你的前端代码 - part4(集成测试)

上一篇文章《测试你的前端代码 - part3(端到端测试)》中,我介绍了关于端到端测试的基本知识,从本文介绍集成测试(Integration Testing)。

一、集成测试

我们已经看过了“测试光谱”中的两种测试:单元测试和端到端测试。实际工作中的测试经常是介于这两种测试之间的,包括我在内的大多数人通常把这种测试叫做集成测试。

二、关于术语

和许多 TDD 爱好者聊过以后,我了解了他们对“集成测试”这个词有一些不同的理解。他们认为集成测试是测试代码边界,即代码对外的接口部分。

比如他们代码中有 Ajax,localStorage 或者 IndexedDB 操作,那其代码就不能做单元测试,这时他们会把这些代码打包成接口,然后在做单元测试的时候 mock 这些接口。当真正测试这些接口的时候才称作“集成测试”。从这个角度来说,“集成测试”就是在纯的单元测试以外,测试与外部“真实世界”相关的代码。

而我和其他一些人则倾向于认为“集成测试”是将两个或多个单元测试综合起来进行测试的一种方法。通过接口把与外部相关的代码打包到一起,再 mock,只是其中的一种实现方式。

我的观点里,决定是否使用真实场景的 Ajax 或者其他 I/O 操作进行集成测试(即不使用 mock),取决于是否能够保证测试速度足够快,并且能够稳定测试(不发生 flaky 的情况)。如果可以确定这样的话,那尽管用真实场景进行集成测试就好了。不过如果很慢或者发生不稳定测试的情况,那还是用 mock 会好一些。

在我们的例子中,计算器应用唯一的真实 I/O 就是操作 DOM 了,没有 Ajax 调用,所以不存在上面的问题。

三、mock DOM

这就引出了一个问题:在集成测试中是否需要 mock DOM?重新思考一下上面我说的标准,使用真实 DOM 是否会使测试变慢呢,答案是会的。使用真实 DOM 意味着要用浏览器,用浏览器意味着测试速度变慢,测试变的不稳定。

那么是不是要么只能尽量把操作 DOM 的代码分离出来,要么只能使用端到端测试了呢?其实这两种方法都不好。还有另一种解决方案:jsdom。一个非常棒的包,用它自己的话说:这是在 NodeJS 中实现的 DOM。

它确实比较好用,可以运行在 Node 环境下。使用 JSDom,你可以不把 DOM 当做 I/O 操作。这一点非常重要,因为要把 DOM 操作从前端代码中分离出来非常困难(实际工作中几乎不可能完全分离)。我猜 JSDom 的诞生就是因为这个原因:使得在 Node 中也可以运行前端测试。

我们来看一下它的工作原理,和往常一样,需要有初始化代码和测试代码。这次我们先看测试代码。不过正式看代码之前请先接受我的歉意。

四、歉意

这一部分是这个测试系列文章中唯一使用指定框架的部分,这部分使用的框架是 React。选择 React 并不是因为它是最好的框架,我坚定地认为没有所谓最好的框架,我甚至认为对于指定的场景也没有最好的框架。我相信的是对于个人来讲,只有最合适,用着最顺手的框架。

而我使用着最顺手的框架就是 React,所以接下来的代码都是 React 代码。但是这里依然说明一下,前端集成测试的 jsdom 解决方案可以适用于所有的主流框架。

ok,现在回到正题。

五、使用 Jsdom

  1. const React = require('react') 
  2.     const e = React.createElement 
  3.     const ReactDom = require('react-dom') 
  4.     const CalculatorApp = require('../../lib/calculator-app') 
  5.     ... 
  6.  
  7.     describe('calculator app component', function () { 
  8.     ... 
  9.       it('should work', function () { 
  10.         ReactDom.render(e(CalculatorApp), document.getElementById('container')) 
  11.  
  12.         const displayElement = document.querySelector('.display') 
  13.  
  14.         expect(displayElement.textContent).to.equal('0') 

注意看第 10 - 14 行,首先 render 了 CalculatorApp 组件,这个操作同时也 render 了 Display 和Keypad。第 12 和 14 行测试了 DOM 中计算器的显示是否是 0(初始化状态下)。

上面的代码是可以运行在 Node 下的,注意到里面用的是 document。我第一次使用它的时候特别惊讶。全局变量 document 是一个浏览器变量,竟然可以使用在 NodeJS 中。在这简单的几行代码背后有着大量的代码支撑着,这些 jsdom 代码几乎是完美地实现了浏览器的功能。所以这里我要感谢 Domenic Denicola, Elijah Insua 和为这个工具包做过贡献的人们。

使用 Jsdom

第 10 行中也使用了 document(调用 ReactDom 来渲染组件),在 ReactDom 经常会使用它。那么在哪里创建的这些全局变量呢?在测试中创建的,见下面代码:

  1. before(function () { 
  2.         global.document = jsdom(`<!doctype html><html><body><div id="container"/></div></body></html>`) 
  3.         global.window = document.defaultView 
  4.       }) 
  5.  
  6.     after(function () { 
  7.         delete global.window 
  8.         delete global.document 
  9.       }) 

代码中创建了一个简单的 document,把我们的组件挂在一个简易 div 上。同时还创建了一个 window,其实我们并不需要它,但是 React 需要。最后在 after 中清理全局变量。

document 和 window 一定要设置成全局的吗?滥用全局变量不论理论和实践的角度都不是个好习惯。如果它们是全局的,那这个集成测试就不能和其他的集成测试并行运行(这里对 ava 的用户表示抱歉),因为它们会互相覆写全局变量,导致结果错误。

然而,它们必须要设置成全局的,React 和 ReactDOM 要求 document 和 window 是全局的,不接受把他们以参数的形式传递。或许等 React fiber 出来就可以了?也许吧,不过现在我们还必须要把 document 和window 设置成全局的。

六、事件处理

剩下的测试代码怎么写呢,看下面代码:

  1. ReactDom.render(e(CalculatorApp), document.getElementById('container')) 
  2.  
  3.    const displayElement = document.querySelector('.display') 
  4.  
  5.    expect(displayElement.textContent).to.equal('0') 
  6.  
  7.    const digit4Element = document.querySelector('.digit-4') 
  8.    const digit2Element = document.querySelector('.digit-2') 
  9.    const operatorMultiply = document.querySelector('.operator-multiply') 
  10.    const operatorEquals = document.querySelector('.operator-equals') 
  11.  
  12.    digit4Element.click() 
  13.    digit2Element.click() 
  14.    operatorMultiply.click() 
  15.    digit2Element.click() 
  16.    operatorEquals.click() 
  17.  
  18.    expect(displayElement.textContent).to.equal('84') 

测试中主要实现的是用户点击 “42 * 2 = ”,结果应该是输出 “84”。这里获取 element 使用的是广为人知的querySelector 函数,然后调用 click 点击。还可以创建事件,然后手动调度,见下面代码:

  1. var ev = new Event("keyup", ...); 
  2.  document.dispatchEvent(ev); 

这里有内置的 click 函数,所以我们直接使用就好了。就是这么简单!

机智的你可能已经发现了,这个测试和前面的端到端测试其实是一样的。但是注意这个测试要快 10 倍以上,并且实际上它是同步的,代码也更容易写,可读性也更好。

但是如果都一样的话,那需要继承测试干嘛?因为这是个示例项目嘛,并不是实际项目。这个项目里面只有两个组件,所以端到端测试和继承测试是一样的。如果是在实际项目中,端到端测试可能包含了上百个单元,而继承测试只包含少量单元,比如包含 10 个单元。所以实际项目中只有几个端到端测试,而可能包含了上百个继承测试。

七、总结

本文中主要介绍了什么:

  • 介绍了使用 jsdom 方便地创建全局变量 document 和 window;
  • 介绍了如何使用 jsdom 测试应用;
  • 介绍了,测试就是这么简单。

点击《测试你的前端代码 - part4(集成测试)》阅读原文。

【本文是51CTO专栏作者“胡子大哈”的原创文章,转载请联系作者本人获取授权】

戳这里,看该作者更多好文

责任编辑:赵宁宁 来源: 51CTO专栏
相关推荐

2017-03-30 07:56:30

测试前端代码

2017-09-14 15:16:56

2021-12-29 10:30:15

JMH代码Java

2023-01-26 00:28:45

前端测试技术

2020-10-13 10:51:10

Linux内核

2020-09-23 12:42:08

Linux

2020-09-15 06:15:23

渗透测试风险评估网络安全

2020-09-11 11:29:34

渗透测试风险评估网络安全

2023-10-27 08:49:00

JCovOpenJDK

2021-06-30 19:48:21

前端自动化测试Vue 应用

2021-10-29 05:53:51

前端测试开发代码

2020-10-10 10:14:42

Linux内核

2020-10-12 10:22:16

Linux内核

2021-06-25 10:57:30

前端自动化测试开发

2021-06-26 07:40:21

前端自动化测试Jest

2009-06-17 13:58:00

JMeter测试EJB

2014-05-07 09:52:35

测试测试人员

2023-05-18 14:01:00

前端自动化测试

2016-12-05 19:16:03

RxJava

2013-04-08 09:28:09

测试
点赞
收藏

51CTO技术栈公众号