一次有缺憾的单元测试修改

2017-10-05

开篇闲扯

本来是想要对pocket-tool进行功能增强的,增加链式写法,不过随便用了一下之前的功能就出现了bug,这是才想起来之前针对DOM操作的单元测试还没写,这次就干脆先把这部分的单元测试给补上,的确是补上了,但是还有一些遗憾,接下来会先对这次DOM模块的单元测试进行思路分析,然后再说一下缺憾有哪些。

先从需求开始

要对DOM进行单元测试,最基本的一个需求就是要获取document对象,但是mocha运行在node环境中,在node中并没有document对象,所以要从获取document开始,我一开始想的是应该有某位大神已经在node中实现了该特性。于是我就顺着这个思路查找了几个。

  1. jsdom
  2. cheerio
  3. mocha - browser support
  4. phantomjs

找到这四个的途径分别是通过阅读官方文档,比如mocha,还有就是看各个开源项目的test,比如jquery,还有一个是自己用过的cheerio。其中有个比较遗憾的就是,看到jquerytest时,只是简单的扫一眼,或者直接去找他们是如何获取document的,一直把他们的运行模式给忽略了,发现最后也是运行在浏览器端的。
接下来要对这四者进行简单的介绍。

再对功能进行分析

jsdom

官方的介绍是 A JavaScript implementation of the WHATWG DOM and HTML standards, for use with node.js 大概就是指:一个为我们提供在node.js上贯彻WHATWG DOMHTML标准的JavascriptWHATWG是一个组织,貌似看着好像是我所需要的,再接着看API的调用方式

1
2
const jsdom = require("jsdom");
const { JSDOM } = jsdom;

诶,好像有点样子,再接着看

1
2
const dom = new JSDOM(`<!DOCTYPE html><p>Hello world</p>`);
console.log(dom.window.document.querySelector("p").textContent); // "Hello world"
1
2
3
const { window } = new JSDOM(`...`);
// or even
const { document } = (new JSDOM(`...`)).window;

是通过传入一段HTML字符串,然后生成一个实例。到这里为止,好像并不能满足我的需求,我不能将window注入到我的项目中,因为这其实是一个伪造的,所以获取到的节点对象,可能并不是真正的document对象,这会影响我最终的测试。所以,只能放弃了。

cheerio

官方的描述是Fast, flexible, and lean implementation of core jQuery designed specifically for the server.其实和jsdom差不多,只不过这个是以jQuery为核心的,也就是说可以通过jQuery的操作方式进行取值。再看看API的调用方式

1
2
3
4
5
6
7
8
const cheerio = require('cheerio')
const $ = cheerio.load('<h2 class="title">Hello world</h2>')

$('h2.title').text('Hello there!')
$('h2').addClass('welcome')

$.html()
//=> <h2 class="title welcome">Hello there!</h2>

其实和jsdom是差不多,好吧,也无法满足我的需求,不过我用过它开发过一个对没有用的class进行筛查的工具,写的太烂,功能没有进行跟进,我这就不贴出来了,哈哈。还是很好用的。
通过上面jsdomcheerio的了解可以发现,其实并不能使用一个模拟的document对象,而是要真正的document对象,于是将思路再转移到了浏览器端。

mocha - browser support

我又回到了看mocha官方网站,这时发现了Mocha can also be installed via Bower (bower install mocha), and is available at cdnjs.,还有Mocha runs in the browser. Every release of Mocha will have new builds of ./mocha.js and ./mocha.css for use in the browser.这就说明了,mocha可以运行在浏览器上,那我其实就不需要在node环境中运行mocha了,只需要写完之后,运行在浏览器上就好了,具体的使用方式就是,引入

1
2
3
4
5
<link href="https://cdn.rawgit.com/mochajs/mocha/2.2.5/mocha.css" rel="stylesheet" />

<script src="https://cdn.rawgit.com/jquery/jquery/2.1.4/dist/jquery.min.js"></script>
<script src="https://cdn.rawgit.com/Automattic/expect.js/0.3.1/index.js"></script>
<script src="https://cdn.rawgit.com/mochajs/mocha/2.2.5/mocha.js"></script>

鉴于我们国内的情况,还是通过bower进行安装一下上面的几个工具吧jquerymocha,我用的断言库是should,自行选择喜欢的就好了。然后再加入mocha的方法,还有测试单元的js文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<div id="mocha"></div>

<script src="https://cdn.rawgit.com/jquery/jquery/2.1.4/dist/jquery.min.js"></script>
<script src="https://cdn.rawgit.com/Automattic/expect.js/0.3.1/index.js"></script>
<script src="https://cdn.rawgit.com/mochajs/mocha/2.2.5/mocha.js"></script>

<script>mocha.setup('bdd')</script>
<script src="test.array.js"></script>
<script src="test.object.js"></script>
<script src="test.xhr.js"></script>
<script>
mocha.checkLeaks();
mocha.globals(['jQuery']);
mocha.run();
</script>

这里都是官方的调用方式,我自己的也差不多,还是看官方的更加清晰一些,当你全部配置完成后,打开html文件就好了,这是就能看见类似这样的页面
https://oss.wengwang.me/images/wengwang_me/hexo/Font-end/1.png
说明成功运行起来,ok,这样就能对有document的功能进行单元测试了,但是,这还不行,如果我要加入CI的话,要怎么在命令行中运行,并且当不通过时退出呢?这时我就继续摸索,于是找到了phantomjs,虽然之前就听说过,但是没有用过,这时就派上用场了。

phantomjs

根据官方的描述PhantomJS is a headless WebKit scriptable with a JavaScript API. It has fast and native support for various web standards: DOM handling, CSS selector, JSON, Canvas, and SVG.吧啦吧啦,其实就像一个没有页面的浏览器,其实还有很多强大的功能,我还没探索,有兴趣的同学可以到它们的官方网站看看这里先暂时借个壳,当然有这个还是不够的,因为运行起来还只是运行了一下,没有任何反应,不会因为某个用例不通过时断开以及抛出错误,这时还要再借助mocha-phantomjs-core
ok,万事具备了,接下来开始整合。

最后进行整合

先把需要的东西安装一下

1
2
3
4
bower install mocha should.js --save-dev // 安装mocha和should端断言库

npm install phantomjs -g // 全局安装 phantomjs
npm install mocha-phantomjs-core -D // 安装mocha-phantomjs-core

这时创建一个index.html,再把需要的工具引入一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<!DOCTYPE html>
<html lang="en">

<head>
<title>DOM DEMO</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="../../bower_components/mocha/mocha.css" rel="stylesheet" />
</head>

<body>
<div id="mocha"></div>

<script src="../../bower_components/should.js/should.js"></script>
<script src="../../bower_components/mocha/mocha.js"></script>
<script src="../../bower_components/jquery/dist/jquery.min.js"></script>

<!-- 这是我想要测试的脚本 -->
<script src="../../dist/pocket-tool.js"></script>
<!-- 这是我想要测试的脚本 -->

<script>
mocha.setup('bdd') // 使用bdd的方式
</script>

<!-- 这是我的用例 -->
<script src="./test/getElement.js"></script>
<script src="./test/addClass.js"></script>
<script src="./test/removeClass.js"></script>
<script src="./test/on.js"></script>
<script src="./test/off.js"></script>
<script src="./test/getDataSet.js"></script>
<script src="./test/chain.js"></script>
<!-- /这是我的用例 -->

<script>
mocha.run();
</script>
</body>

</html>

入口搞定了,单元测试用例的写法都和mocha的一样,如果想要支持es6的方法,只要再引入babel-polyfill就好了。这些都搞定后,就是要如何运行了,先在命令行中进行一下测试phantomjs ./node_modules/mocha-phantomjs-core/mocha-phantomjs-core.js ./test/phantomjs/index.html spec '{\"useColors\":true}', 通过mocha-phantomjs-core的方式进行运行后面的index.html,一切顺利的就能看到
https://oss.wengwang.me/images/wengwang_me/hexo/Font-end/1.png
这一切又是那么的熟悉。这时再到package.jsonscript添加一下就好

1
2
3
"scripts": {
"test:phantomjs": "phantomjs ./node_modules/mocha-phantomjs-core/mocha-phantomjs-core.js ./test/phantomjs/index.html spec '{\"useColors\":true}'",
}

这次的缺憾

因为我的项目中有两个部分,一个是在nodejs中使用mocha一个是在phantomjs,想要把这两者做一个结合,这样方便CI于是我有找到了karma但是好难整合,不知道是不是我的姿势不对,最后还是使用package.jsscript进行结合

1
2
3
4
5
"scripts": {
"test": "npm run test:mocha && npm run test:phantomjs",
"test:mocha": "npm run dist && mocha test/mocha",
"test:phantomjs":"phantomjs ./node_modules/mocha-phantomjs-core/mocha-phantomjs-core.js ./test/phantomjs/index.html spec '{\"useColors\":true}'"
}

结果和我想要的一样,但是还要加入覆盖率统计,这时就蛋疼了,这里我使用了istanbul,对运行在nodejs上的可以使用

1
istanbul cover _mocha test/mocha"

但是在phantomjs中的就有点麻烦了,虽然有mocha-phantomjs+mocha-phantomjs-istanbul,但是貌似mocha-phantomjs-istanbul并不生效,感觉这有点繁琐了,想要找个办法,将两个方案进行整合。

结语

这次还算可以,只是运行在phantomjs上的无法计算覆盖率,还有就是没有通过karam将两个进行整合,看来之后再继续一段单元测试之路了,之前我觉得单元测试没有什么必要,但是在这次中是对工具进行功能上的升级,就庆幸之前有写了单元测试,这样就不怕有严重的影响了。对了,希望有好的方案的同学,可以通过留言跟我一起谈论一下。

下面是我的微信

欢迎骚扰

ww1o01