首页 > 对Javascript单元测试的不解,求指导!

对Javascript单元测试的不解,求指导!

我从来没写过JS的单元测试,但是对单元测试有了解,像jasmine、mocha;看过一些基础的例子,我有一个疑惑,如果我要测试的函数里有DOM操作怎么办(还是说我要将业务逻辑部分和DOM分开)?还有用Seajs我改如何写测试?还望各位高手不吝赐教.万分感谢!


如果涉及到 DOM 操作,严格来说这就不是单元测试的范畴了,因为 DOM 并不属于 Javascript 语言本身(或者更严格地说,应该是 ECMAScript 单元测试,而不是 Javascript 单元测试)。

若上述描述很难让你想象清楚,那么你就这么想:在单元测试的范畴里,我们测试的是 Javascript 里属于 ECMAScript 的部分。从这个角度来说,DOM 是 ECMAScript 用来操作和交互的一个 API。

在单元测试里,如果业务代码有外部 API 的依赖(比如本题所关注的 DOM),那么我们可以使用 Mock/Stub/Spy/Double 等技术,这几个词在一定程度上有共通的部分,但是各自也有细节上的差别。无论是 QUnit/Jasmine/Mocha,还是别的什么框架,都会有类似方面的扩展/插件/教程等,建议自行搜索学习一下。

我用简单的方式给你解释一下。

由于单元测试的运行环境受限(先不讲运行于浏览器的情况下),所以访问 DOM 是很困难的,但是我们可以伪造 DOM 对象来满足测试时的依赖。前面说的 Mock 等等就是用来伪造依赖对象的技术手段,它们有的用来伪造对象生成,有的用来伪造对象的行为(方法及返回值)等等。

从较高层面来理解,“伪造对象”和“鸭式辨型”或其他相似概念有一定的相似之处。你的业务代码是要操作 DOM 的,但是测试的时候没有 DOM 可用,如果我们创造一个类似 DOM 的对象呢(甚至不需要创造整个 DOM,仅仅是模拟当前待测代码需要的一个类似 DOM 的对象即可)?要验证你的代码是否如预期那样工作,并不是非得要完全等价的环境的,我们可以把外部环境的依赖条件抽象成基本环境(单元测试的运行环境)可以创造的东西就好了。

以上是从单元测试的角度来回答你这个问题,这样的方法当然不是实际中最简单,最理想的。要更进一步,你需要跳出“单元测试”的框框,进入所谓“集成测试”的世界。

一旦你需要测试的东西需要和实际运行环境(比如浏览器,比如 DOM)紧密结合起来,单元测试就不管用了。我们需要的是一种自动化的方式,它能够满足以下几个条件:

  1. 设置简单,可以很容易的把业务代码与测试代码分离(在组织结构上)或集成(在测试环境搭建上)
  2. 能够很好的模拟各种实际环境(多浏览器,多运行环境支持)
  3. 提供简单好用的 API 供开发者编写能够模拟用户交互操作的脚本
  4. 可以手动运行,也可以自动运行,支持 CI 集成(你不会想永远等待漫长的脚本运行吧)
  5. 其他(根据实际情况决定)

集成测试有很多种叫法,比如说 Angular 的 end-to-end integration testing(端到端集成测试)等。不同的集成测试套件或许各有差异,但根本上就是一个可以运行在实际环境中的代码运行器。在此基础上提供一些 API,你可以模拟用户的操作(比如点击按钮,填写表单等等),然后运行器针对你的代码来运行这些脚本并检查页面的变化(这些变化应该是什么样子的,当然也是你在脚本里指定好的,类似单元测试里的断言(assertions)),根据检查结果来判定测试成功与否。

这种测试可以满足你测试 DOM 操作的要求,但是它明显和单元测试的层级不同。单元测试测试的是代码的内部表现,其着重点是隔离外部依赖以保证测试的“纯粹性”;但是集成测试是通过模拟真实环境的运行来侧面反应你的代码,乃至整个系统的行为表现。

或许你想测试的是“一个方法能不能删除一个 DOM 节点”,但是集成测试的方式是通过“在用户点击‘删除’按钮之后判断页面上还有没有对应的目标”来实现的。有点“曲线救国”的意思。

QUnit/Jasmine/Mocha 都提供了 TestRunner,可以直接运行在浏览器里,你可以在测试页面里手动写一些 HTML 当作测试用的 DOM 对象,这其实是这些单元测试框架提供的集成测试的运行环境而已。

当然了,严格地说集成测试已经超出了你问题的界限,但遗憾地是很多 JS 开发者总是把 DOM(或其他 API)同 Javascript 语言本身混为一谈,所以总是觉得单元测试这东西对 Javascript 来说可有可无的(因为实现起来不容易)或者觉得太复杂(因为本身已经超出了单纯的单元测试的范畴,或者需要介入进阶的技术)。因此在这里多扯两句,希望为楼主解惑。答案本身涉及了不少概念,大概会有些泛泛而谈,等楼主先做个大致的了解之后继续问更具体的要求吧。

===补充===

没注意看到你后面说的 SeaJS 测试的问题,补充几句。

我没怎么用过 SeaJS,以前都是使用 RequireJS 的,但据我所知差不了多少。至于测试,你怎么写代码就怎么测试,这些工具只是帮你做了代码依赖管理嘛,并不会影响到测试本身。


说到操作dom的单元测试。原生js是没发进行这种测试的。不知题主用过react没,react组件渲染到dom中,然后用jsdom可以进行单元测试。包括react的dom的操作。
这里有篇react单元测试的例子,里面包含了操作react dom的例子
react单元测试

【热门文章】
【热门文章】