使用 mocha 和 chai 进行基于浏览器的 javascript 异步测试

目录
[隐藏]

基于 BDD (行为驱动开发) 的 UI 相关的功能性测试,特别是涉及到与后端数据交互的情况下,需要浏览器环境和对 ajax 的支持,对于涉及动画的交互,还需要延时执行测试代码。这里提到了几个基本要求:

  1. 浏览器环境支持(DOM/DOM JavaScript API/CSSDOM 等)
  2. ajax 支持
  3. 异步测试支持

下面针对这样的诉求,介绍使用 mocha、chai 和 PhantomJS 方案的相关测试工具/库的安装与测试环境的构建。

1. 相关测试工具/库简介

1.1 mocha

mocha 是近年来非常流行的 JavaScript 测试框架。它 支持浏览器环境,并且支持异步测试(done 回调Promise),参考如下:

http://mochajs.org/#running-mocha-in-the-browser

http://mochajs.org/#asynchronous-code

1.2 chai

chai 是一个常常被拿来与 mocha 一起使用的断言库,提供了丰富的断言接口,可以对各种结果进行断言。

http://chaijs.com/api/

1.3 PhantomJS

PhantomJS 提供了一个基于 WebKit 内核的无界面的浏览器运行环境,使用它提供的 API,可以通过 JavaScript 自动化的实现基于浏览器的各种操作。于是基于它的 API,进行无界面的自动化测试也相应变的简单。

http://phantomjs.org/

http://phantomjs.org/headless-testing.html

1.4 mocha-phantomjs

mocha-phantomjs 是一个 通过 PhantomJS 执行 mocha 浏览器环境测试的工具库。它使用 PhantomJS 的浏览器环境,通过事件监听的方式检测 mocha 测试的执行过程。

https://github.com/nathanboktae/mocha-phantomjs

mocha-phantomjs-core

mocha-phantomjs-coremocha-phantomjs的核心依赖库。作者将它单独提取出来,是因为它也可以支持 SlimerJS
SlimerJS 是基于的 Gecko 内核(Firefox)的与 PhantomJS 的 API 几乎相同的工具,而且SlimerJS在执行过程中默认会启动有界面的浏览器窗体,可以看到整个执行过程。

https://github.com/nathanboktae/mocha-phantomjs-core

http://www.slimerjs.org/

2. 测试环境构建

这里我们采用 mocha 作为测试框架,使用 chaiExpect 断言风格 API,使用 mocha-phantomjs 模拟浏览器和 ajax 环境。

2.1 工具库安装

进入到项目目录下,执行命令:

npm i mocha chai mocha-phantomjs-core mocha-phantomjs --save-dev

2.2 创建测试目录

新建 test 目录作为测试目录,参考 mocha 的浏览器环境示例,在测试目录下新建 index.html 文件作为测试入口。index 文件内容参考:

<!DOCTYPE html>
<html lang="zh-cn">
<head>
    <meta charset="utf-8">
    <title>mocha-chai-test</title>
    <link id="bscss" href="../node_modules/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet">
    <link href="../node_modules/mocha/mocha.css" rel="stylesheet">
</head>
<body>
    <div id="mocha"></div>
    <div class="container">
        <div class="row">
            <div class="col-lg-2">
                <div class="input-group">
                    <input type="text" class="form-control" id="test">
                    <div class="input-group-btn">
                        <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">
                            <span class="caret"></span>
                        </button>
                        <ul class="dropdown-menu dropdown-menu-right" role="menu">
                        </ul>
                    </div>
                </div>
            </div>
        </div>
    </div>
</body>
<script src="../node_modules/jquery/dist/jquery.min.js"></script>
<script src="../node_modules/bootstrap/dist/js/bootstrap.min.js"></script>
<script src="../node_modules/mocha/mocha.js"></script>
<script src="../node_modules/chai/chai.js"></script>
<!-- 测试文件引入 -->
<script src="../src/bootstrap-suggest.js"></script>
<script src="helper/setup.js"></script>
<script src="test.js"></script>
<script src="helper/run.js"></script>
</html>

3. 异步测试代码的编写

mocha 是单线程顺序执行测试的,这对于不相关的多模块测试来说,可能会使得测试时间过长(这种情况下是可以并发执行的,可参考 ava)。但是它的每一步测试都要等待前一个测试执行结束,这也使得异步测试可以方便地顺序书写。

在测试目录下新建测试文件,和书写浏览器环境中的功能代码类似的,书写测试代码,并在 index.html 中引入。

我们新建 test.js 作为示例,主要介绍如何书写异步的测试。
为了简单,这里没有使用任何模块化和需要构建的内容。
为了方便的异步测试书写,采用了 jQuery$.Deferred 异步解决方案。当然,你完全可以使用 ES6/ES7PromiseGenerator,然后使用 babel 进行编译。

首先初始化(setup.js):

window.mocha.setup({
    ui: 'bdd',
    timeout: 30000 //异步测试时间一般较长,根据实际测试情况适当设置超时时间
});

window.expect = window.chai.expect;

/**
 * 执行异步的验证,返回 deferred 对象以便于持续的执行
 * @param  {Function} callback 要异步执行的方法
 * @param  {Number}   timeout  超时时间,超时则直接 resolve
 * @param  {Function} done     mocha 异步 done
 * @return {Deferred}
 */
window.lintAsync = function(callback, timeout, done) {
    timeout = timeout || 300;
    var $d = $.Deferred();

    try {
        var $p = callback($d);
        done && $p.then && $p.then(done);
    } catch (err) {
        done && done(err);
    }

    setTimeout(function() {
        $d.resolve();
    }, timeout);

    return $d;
}

然后编写异步测试代码(test.js),示例:

var $input = $('#test');
var $dropdown = $input.next().find('ul');
var lintAsync = window.lintAsync;

describe('测试(首次从 URL 获取数据)', function() {
    var param = {
        url: "data.json",  //test 目录下存放该 api 数据文件
        idField: "userId",
        keyField: "userName"
    };

    //采用 done 回调的方式支持异步执行
    it('基本初始化,数据请求成功测试', function(done) {
        lintAsync(function($d) {
            $input.bsSuggest(param).on('onDataRequestSuccess', function(e, result) {
                expect(result).to.have.property('value').with.length(31);
                $d.resolve();
                done();
            });
        });
    });

    describe('各种输入操作模拟', function() {
        it('触发 keyup/keydown 事件', function() {
            //返回为有 thenable 支持的 Promise 对象,也可以支持异步执行
            return lintAsync(function() {
                  $input.trigger('keyup').trigger('keydown');
              })
            .then(function() {
                expect($dropdown.is(':visible')).to.be.true;
                expect($dropdown.find('tr').length).to.be.ok;
            });
        });

        //todo more test...
    });
});

最后,执行测试:

mocha.checkLeaks();
mocha.globals(['jQuery']);
mocha.run();

以上的示例可参考 bootstrap-suggest-plugin 项目的测试。

4. 测试的执行

4.1 使用 mocha-phantomjs

使用 mocha-phantomjs 执行。执行命令:

.\node_modules\.bin\mocha-phantomjs test/index.html  -R dot 

命令行中即会返回测试的结果。

mocha-phantomjs-test-result

4.2 使用 mocha-phantomjs-core

参考 mocha-phantomjs-core 的文档说明,也可以以如下方式执行测试:

.\node_modules\.bin\phantomjs .\node_modules\mocha-phantomjs-core\mocha-phantomjs-core.js test/index.html

使用 SlimerJS 执行测试:

.\node_modules\.bin\slimerjs .\node_modules\mocha-phantomjs-core\mocha-phantomjs-core.js test/index.html

更多的参数参考:

https://github.com/nathanboktae/mocha-phantomjs

https://github.com/nathanboktae/mocha-phantomjs-core

4.3 使用浏览器

通过浏览器直接打开 test/index.html,也可以看到测试执行的情况。由于涉及到 ajax 请求,应当在 webServer 下打开页面。

浏览器中的示例: http://lzw.me/pages/demo/bootstrap-suggest-plugin/test/

5. 其他的说明

关于 mocha 的执行顺序

在 describe 下的 it 会优先于同级别的 describe 内的 it 执行,这点应当注意。

关于 ajax 相关的测试

由于网络环境的复杂性,如无必要的复杂数据请求测试,可自行挂在 mock 方法。

  1. API 较少的情况下,提供对应 API 的文件。这是最简单的方式。
  2. 简单 mock。例如重写全局 ajax/fetch 方法,以 jQuery 为例:
function mockAjaxSimple(options) {
    //todo..
}
$.ajax = function(options) {
   return $.Deferred().resolve(mockAjaxSimple(options));
};
  1. 复杂的 mock。采用各种 mock 插件。例如 jquery.mockjax

基于 ES6+ 的测试

由于是基于浏览器的测试,
使用 webpack 或其他模块化构建工具编译为 ES5 的代码输出,然后对输出后的页面作测试即可。测试代码也可以使用 ES6/ES7 语法,然后使用 babel 编译。

关于 PhantomJS 和 SlimerJS 的 CDN 安装

对于 PhantomJS 的依赖安装,如下载超时,可先执行如下命令:

export PHANTOMJS_CDNURL=https://npm.taobao.org/dist/phantomjs
npm i phantomjs

windows 下执行:

set PHANTOMJS_CDNURL=https://npm.taobao.org/dist/phantomjs
npm i phantomjs

对于 SlimerJS 的安装,存在同样的问题,可如下命令后再执行安装:

set PHANTOMJS_CDNURL=https://download.slimerjs.org/releases
npm i slimerjs

在 CDN 也搞不定的情况下,另外还有一种变通的安装方式。通过命令行提示,使用迅雷等 P2P 工具下载对应的 zip 包,解压到命令行提示中下载的目录,解压,重命名解压目录为对应提示的临时名称(视实际情况操作)。然后重新执行安装命令即可顺利安装。

download-slimerjs-cmd

6. 相关参考

http://mochajs.org

http://chaijs.com/api/bdd/

https://github.com/nathanboktae/mocha-phantomjs

https://github.com/nathanboktae/mocha-phantomjs-core

http://phantomjs.org/headless-testing.html

http://www.slimerjs.org/

点赞 (2)

发表评论

电子邮件地址不会被公开。 必填项已用*标注