产品战略专家梁宁确认出席AICon北京站,分享AI时代下的商业逻辑与产品需求 了解详情
写点什么

Dojo 单元测试框架 DOH 介绍

  • 2011-06-10
  • 本文字数:4831 字

    阅读完需:约 16 分钟

单元测试的重要性已毋须多言,无论是从保证软件开发质量,还是从节约软件后期维护成本来说,单元测试都是最佳实践。而在敏捷编程领域,随着 TDD(测试驱动开发)被越来越多的开发者所接受,单元测试已经成为开发过程中举足轻重的一部分。

编写单元测试离不开成熟的单元测试框架,由于 JUnit 框架的成功,Java 开发者对于单元测试的接受程度非常高。而 Web2.0 前端开发的单元测试一直以来是一块不太受重视的领域,导致这个状况的有很多:前端开发中逻辑和界面耦合度高、Javascript 的模块概念单薄、Javascript 运行环境(浏览器)不统一等;最主要的原因是缺乏成熟的单元测试框架,用来支持 Web 开发的特性(Ajax,DOM 等)以及 Web 前端的自动化单元测试,然而 Dojo 中的 DOH 工具改变了这个现象。

DOH 简介

Dojo 作为一个成熟的 Javascript 开发工具集,提供了强大的 Javascript 单元测试工具——DOH(Dojo Objective Harness)。DOH 主要是由 Dojo 的创始人 Alex Russel 主持开发,目的就是要针对 Web 前端开发者提供一个有如下特点的测试框架:

  1. 提供用户界面:JUnit 中的红条测试失败、绿条测试通过,大家都已经很熟悉了,DOH 也有类似的用户界面,用户在测试时更加一目了然;
  2. 平台无关:DOH 并不依赖某种浏览器平台,甚至不依赖于浏览器;用户可以根据自己的需要在命令行进行 Javascript 的自动化单元测试;
  3. 支持 Ajax:Ajax 编程在 Web 前端开发中是必不可少的一环,DOH 最有价值的一个特性就是支持 Ajax 的测试;
  4. 不只适合与于 Dojo,可用于任何 JavaScript 程序的单元测试。

本文将以 Dojo1.6.1 版本为例,介绍如何使用 DOH 编写测试用例。

DOH 初体验

Dojo 的核心库(dojo)、控件库 (dijit) 以及一部分的扩展库 (dojox) 都自带了比较完备的测试用例,所以在了解如何编写 DOH 测试用例之前,运行一下 Dojo1.6 版本中已有的测试,可以对 DOH 有个大致的了解。

首先下载 Dojo1.6.1 ,DOH 测试框架就在 dojo-release-1.6.1-src/util/doh 文件夹下,其中 runner.html 页面就是基于浏览器的 DOH 测试用户界面。本文中的 http 服务器使用 Apache2.2,有关 Apache 的配置可以参照这里,首先我们来运行一下最常被使用的dojo.query 的测试用例。dojo.query 的测试模块为test._base.query,在浏览器上运行DOH 测试用例非常简单,只要一个url 即可: http://localhost/dojo161/util/doh/runner.html?testModule=tests._base.query ,dojo161 是在 Apache 中设置的虚拟路径,指向 dojo-release-1.6.1-src 目录;下图是 test._base.query 模块的测试结果:

左边的是测试用例列表,可以看到 test._base.query 测试模块里含有两组测试用例:test.base.query 和 test.base.NodeList,同事还显示了该测试集消耗的时间,右边是测试用例的日志。与 JUnit 相同,绿色表示通过测试,而红色反之。

这里需要重点介绍的的是 testModule 参数:DOH 中的测试对象称为测试模块,测试模块中包含测试用例。DOH 提供了两种载入测试模块的方式,一种是直接载入声明了名称的测试模块, 下面的代码声明了名为 test._base.query 的测试模块,包含两组用例:

复制代码
dojo.provide("tests._base.query");
if(dojo.isBrowser){
doh.registerUrl("tests._base.query", dojo.moduleUrl("tests", "_base/query.html"), 60000);
doh.registerUrl("tests._base.NodeList", dojo.moduleUrl("tests", "_base/NodeList.html"), 60000);
}

在测试模块没有直接提供模块名的情况下,DOH 会将 testModule 参数作为路径载入该路径下的测试模块, 例如当 testModule = tests._base 的时候,DOH 将载入 tests/_base/ 下的测试模块,如 test._base.query、test._base.html、test._base.store…等等。

一个简单的 DOH 测试用例

运行了 Dojo 自带的测试用例之后,想必大家对 DOH 有了初步的了解,不过仅仅运行 Dojo 自己的用例怎么会过瘾呢?接下来以一个简单的 DOH 测试用例作为例子,加深对 DOH 的理解。首先在 dojo 工具包的根目录下创建 myTests 文件夹,用来存放测试用例:

之后在 myTests 文件夹下创建 dojoTest.js,请注意模块名与文件路径相对应:

复制代码
dojo.provide("myTests.dojoTest");
doh.register("easyTests", [
function javascriptTest(){
doh.assertEqual(Math.pow(5, 3), 125);
doh.assertTrue(123 == "123");
doh.assertFalse(99999999 > Infinity);
}
]);

上面的代码注册了一个叫 easyTests 的测试用例,现在这个用例里只有一个叫 javascriptTest 的测试。这里用到了 DOH 中的三个断言:doh.assertEqual、doh.assertTrue、doh.assertFalse,如果觉得断言函数名太长,DOH 也提供了 doh.is、doh.t、doh.f 来代替前面三个断言。

细心的读者可能已经注意到 doh.register 的第二个参数是个数组,所以在 easyTests 下还可以继续添加测试,这次我们来测试 dojo.string 的 trim 和 rep 方法:

复制代码
dojo.provide("myTests.dojoTest");
dojo.require("dojo.string");
doh.register("easyTests", [
function javascriptTest(){
...
},
function dojoStringTest(t){
t.is("text", dojo.string.trim(" text \n"));
t.is("xyxyxy", dojo.string.rep("xy", 3));
}
]);

在添加第二个测试之前需要先载入了 dojo.string 模块。在测试中除了 assertEqual 用 is 简写代替以外,doh 也变成了 t,这是因为测试函数的默认参数就是 doh。

在 JUnit 中,setUp 和 tearDown 做了测试的初始化和结束清理工作。在 DOH 也支持这样编写方式:

复制代码
dojo.provide("myTests.dojoTest");
dojo.require("dojo.string");
doh.register("easyTests", [
...,{
name: "Test String Substitute",
_templateString: "",
setUp: function(){
this._templateString = "Dojo ${0} released at ${1}";
},
runTest: function(t){
t.is("Dojo 1.6.1 released at 2010/05/02", dojo.string.substitute(this._templateString, ["1.6.1", "2010/05/02"]));
},
tearDown: function(){
this._templateString = "";
}
]);

name 为该测试的名字;setUp 在 runTest 之前运行;tearDown 在 runTest 之后运行,runTests 是该测试的主体。这里值得注意的是自定义变量 _templateString,方便在 setUp、runTests、tearDown 中使用。

好了,让 DOH 来运行一下这个测试模块, http://localhost/dojo161/util/doh/runner.html?testModule=myTests.dojoTest

与 HTML 集成的 DOH 测试用例

上一节介绍的 DOH 测试用例是比较独立的,但是 Javascript 天生就是要与浏览器、DOM 打交道的,比如 DOM 节点选取功能或拖拽功能的测试就不能独立于 HTML 之外。所以 DOH 测试用例也可以写在 HTML 文件里,直接对 HTML 做操作。接下来我们将编写一个含有 DOH 测试用例的 HTML 文件。

在 myTest 目录下创建 domQuery.html,首先导入必要的 dojo.js 和 runner.js 文件:

复制代码
...
<script type="text/javascript" src="../dojo/dojo.js" djConfig="isDebug: true"></script>
<script type="text/javascript" src="../util/doh/runner.js"></script>
...

然后在 dojo.ready 函数里注册测试用例,注册完毕后不要忘了调用 doh.run() 运行测试:

复制代码
<script>
dojo.addOnLoad(function(){
doh.register("domQueryTest", [
function testQueryByTag(){
doh.is(2, dojo.query("span").length);
},
"doh.is('h3', dojo.query(':first-child', '_foo')[0].innerHTML)",
"doh.is(3, dojo.query('>', '_foo').length)",
...
]);
doh.run();
});
</script>

对于非常简单的测试,如第二个和第三个测试,可以直接用 string 来表示;最后,添加一些测试用的 HTML 代码:

复制代码
<body>
<div class="foo bar" id="_foo">
<h3>h3</h3>
<span id="foo"></span>
<span></span>
</div>
</body>

完成的 HTML 文件并不能被 DOH 的 UI 直接使用,使用 doh.registerUrl 将 HTML 页面注册为测试用例,之后就是运行 DOH 的 runner.html 了。

复制代码
dojo.provide("myTests.domQuery");
try{
doh.registerUrl("myTest.domQuery", dojo.moduleUrl("myTests", "domQuery.html"))
}catch(e){}

集成的 DOH 测试模块

至此,您应该已经了解如何编写单个测试文件。编写测试用例并不困难,而当测试用例越来越多的时候,可以把相关的测试用例集成在一起进行测试,这样就提高了测试的自动化效率。集成测试用例非常简单:只要把已经声明的测试用例用 dojo.require 方法载入到一个模块文件,通常命名为 module.js,当 dojo.require 引入这些文件时,也注册了这些测试。

myTests/module.js

复制代码
dojo.provide("myTests.module");
try{
dojo.require("myTests.dojoTest");
dojo.require("myTests.domQuery");
}catch(e){}

myTests.module 包含了两组测试用例:

手动的给 /doh/runner.html 添加 testModule 参数是个比较烦人的工作,毕竟大多数人是不记得测试模块名称的。在 Dojo 里比较常用的做法是在每个测试模块的同级目录下,添加一个 runTests.html 文件,该文件的作用就是重定向到 runner.html:

myTests/runTests.html

复制代码
<html>
<head>
<title>MyTest Runner</title>
<meta http-equiv="REFRESH" content="0;url=../util/doh/runner.html?testModule=myTests.module">
</head>
<BODY>
Redirecting to D.O.H runner.
</BODY>
</hmtl>

测试 Ajax 异步调用函数

Web2.0 应用中很多功能都是通过 Ajax 异步调用完成的。DOH 提供了 doh.Deferred 对象对一步操作的测试进行支持。doh.Deferred 的使用方式与 dojo.Deferred 类似,用户只要在含有异步调用的单元测试中返回 doh.Deferred 的对象即可。下面代码中的 delay 函数模拟了一个异步调用:延迟触发 count++,之后返回 dojo.Deferred 对象;而在对 delay 的测试函数中给 delay 添加了回调函数,回调函数中的 doh.Deferred 对象的 callback(true) 就表示回调成功。 dojo.Deferred 是 dojo 的 Ajax 调用的核心思想,有兴趣的读者可以看看翻译的 dojo 的官方教程

复制代码
dojo.provide("myTests.delayTest");
function delay(ms){
var count = 0;
var deferred = new dojo.Deferred();
setTimeout(function(){
count++;
deferred.callback(count);
}, ms);
return deferred;
}
doh.register("callbackTest", [
{
name: "Simple ajax call test",
timeout: 5000,
runTests: function(){
var dd = new doh.Deferred();
delay(1000).then(function(res){
doh.is(res == 1);
dd.callback(true);
})
return dd;
}
}
]);

使用命令行运行 DOH

前面已经提到过 DOH 并不依赖于浏览器 UI,我们也可以通过命令行来运行测试模块,定位到 util/doh/ 下,运行以下命令行:

复制代码
java -jar ../shrinksafe/js.jar runner.js testModule=myTest.module

这里需要说明的是,runner.js 是通过 dojo/util 工具包里的 Javascript 的引擎——Rhino 执行的。有关 Rhino 的信息可以看这里: http://www.mozilla.org/rhino/

结束语

尽管前端开发的单元测试还不是那么普及,但随着 Web 程序的日益庞大,维护成本越来越高,前端开发的自动化测试将会占据举足轻重的地位。DOH 是一个灵活而且强大的单元测试框架。它将测试用例模块化为可以单独加载的文件,并提供函数以将测试集成为测试模块,此外还提供了一系列断言 API。

最后,DOH 并不是一个 Dojo 专有的测试框架,也可以用于测试其他的 Javascript 工具包,当然它与 Dojo 的配合使用是最简捷的。DOH 的确是 Dojo 开发者测试 JavaScript 代码的最完整和最有效的框架。

参考资料


感谢张凯峰对本文的审校。

给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家加入到 InfoQ 中文站用户讨论组中与我们的编辑和其他读者朋友交流。

2011-06-10 00:003966

评论

发布
暂无评论
发现更多内容

【案例】服务邮政快递业安全监管 星环科技助力国家邮政局“绿盾”大数据平台建设

星环科技

万人连麦的幕后技术详解

拍乐云Pano

Fil行情:投资fil的成本有哪些?

区块链 分布式存储 IPFS fil fil成本

阿里高工从入门,基础,进阶到项目实战,全面讲解spring boot

Java 程序员 架构 面试 spring Boot Starter

【IT运维】快速解决IT疑难故障就用行云管家!

行云管家

系统运维 堡垒机 IT运维

Vue进阶(二):Vue 项目文件结构介绍

No Silver Bullet

Vue 8月日更 项目结构

Vue3异步数据加载组件:suspense

devpoint

Vue 3 8月日更

客户选型零代码软件到底在对比哪些方面?

明道云

斯图飞腾产品升级!Stratifyd数据分析平台全新改版

分布式性能测试框架用例方案设想(三)

FunTester

性能测试 接口测试 测试框架 测试开发

全球生态合作伙伴纷至沓来 解码AppGallery 2021“期中成绩单”

叶落便知秋

华为

【前端 · 面试 】HTTP 总结(三)—— HTTP 请求方法

编程三昧

面试 大前端 HTTP 8月日更 HTTP方法

Express Flutter SDK 全面支持空安全

ZEGO即构

flutter 大前端 音视频 空安全

基于docker的分布式性能测试框架功能验证(三)

FunTester

分布式 性能测试 接口测试 测试框架 测试开发

声网Agora发布教育信息化解决方案 助力教育公平提效

声网

人工智能 在线教育

架构实战营 模块四作业

孫影

架构实战营 #架构实战营

「SQL数据分析系列」15. 元数据

Databri_AI

sql 脚本 元数据

赋能后疫情时代的商业创新,用友BIP的力量

海比研究院

使用SpringAop对方法进行增强

捡对象的cy

spring aop

体验设计落地的四个原则和十个步骤

石云升

用户体验 关键时刻 体验设计 8月日更

Vue进阶(一):Vue 学习资料汇总

No Silver Bullet

Vue 8月日更

Snowflake如日中天是否代表Hadoop已死?大数据体系到底是什么?

阿里云大数据AI技术

鬼斧神工!阿里架构师把多线程编程精华全部总结到这份《Java并发手册》里面了

Java 编程 架构 面试 计算机

打造区块链“三大平台” 助推数字化转型

CECBC

精彩回顾 | 阿里云 Serverless Developer Meetup 杭州站亮点有这些!

阿里巴巴云原生

阿里云 Serverless 云原生 Meetup

用好这两个小工具,制作乐谱更高效!

懒得勤快

他是如何被公司辞退,再到1000个小时后拿到阿里巴巴offer的?

Java架构师迁哥

格物致知,零代码训练营第三期顺利结业

明道云

我国数字经济规模已达41万亿元 总量跃居世界第二

CECBC

能力圈与焦虑:焦虑是因为自己能力不够吗?

非著名程序员

个人成长 提升认知 焦虑 8月日更

架构实战营 毕业设计

竹林七贤

Dojo单元测试框架DOH介绍_Java_阮奇_InfoQ精选文章