阿里、蚂蚁、晟腾、中科加禾精彩分享 AI 基础设施洞见,现购票可享受 9 折优惠 |AICon 了解详情
写点什么

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:003812

评论

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

2023清华博士团暑期社会实践项目圆满结束,合合信息助力科技人才发展

合合技术团队

人工智能 清华大学 博士

直播|深入解析 StarRocks 存算分离--云原生湖仓 Meetup#2

StarRocks

数据库 云原生 存算分离 国产数据库 湖仓一体

CI+JUnit5并发单测机制创新实践 | 京东物流技术团队

京东科技开发者

测试 高并发 单元测试 并发测试 企业号 8 月 PK 榜

关于MYSQL引擎在物理层面存储那些事

谐云

助力农村金融机构数字化转型,原点安全将出席“第十三届中国农村金融机构信息化发展创新大会”

原点安全

数字化转型 农村金融机构

有自动化运维功能的堡垒机有哪些?大家推荐哪款?

行云管家

高可用 堡垒机 IT运维 自动化运维

【华秋推荐】新能源汽车中的T-BOX系统,你了解多少?

华秋电子

新唐

探索未知,即刻搭建AI原生应用!WAVE SUMMIT Workshop等你来参加

飞桨PaddlePaddle

人工智能 百度飞桨 百度AI WAVE SUMMIT

如何选择适合自己的文件传输工具

镭速

文件传输 文件传输工具

MySQL—修改数据库root用户密码

java易二三

Java MySQL 数据库 程序员 计算机

TiDB 源码编译之 TiUP 篇

TiDB 社区干货传送门

版本测评 新版本/特性解读 7.x 实践

安全易用的运维会诊平台选哪家?可以免费试用吗?

行云管家

运维 IT运维 运维会诊

情景规划与财务建模2.0,如何促进企业全面预算管理的实施

智达方通

智达方通 全面预算管理 财务建模 情景规划

基于大模型的数据血缘异常归因分析

奇点云

元数据 黑客马拉松 奇点云 对象体系

性能测试最佳实践的思考

FunTester

微信技术分享:揭秘微信后台安全特征数据仓库的架构设计

JackJiang

网络编程 即时通讯 IM

MySQL char和varchar区别

java易二三

MySQL 编程 程序员 计算机

面试官:说说Spring中@NotEmpty、@NotBlank、@NotNull 的区别和使用

java易二三

编程 程序员 面试 计算机

TIKV节点数据文件误删后不更换服务器快速恢复

TiDB 社区干货传送门

管理与运维 故障排查/诊断

INFINI Labs 产品更新 | Easysearch 支持 SQL 查询、Console 告警功能支持邮件等多渠道

极限实验室

sql console 邮件 告警 easysearch

直播源码连麦技术功能分享,你要的这里全有

山东布谷网络科技

直播源码

大模型时代下的我们,破茧重生探索新开发范式!|WAVE SUMMIT 开源论坛

飞桨PaddlePaddle

人工智能 百度 开发者 百度飞桨 WAVE SUMMIT

环路检测在风控领域的应用实践丨 Fabarta 技术专栏

Fabarta

大数据 算法 图分析 智能风控 风控算法

【SOP】最佳实践之 TiDB 业务读变慢分析

TiDB 社区干货传送门

性能调优 集群管理 管理与运维 故障排查/诊断

Placement Rules in SQL 使用案例

TiDB 社区干货传送门

新版本/特性解读 6.x 实践

Lighting web 测试使用

TiDB 社区干货传送门

迁移 管理与运维 备份 & 恢复 6.x 实践

新利好带动 POSE 持续上扬,月内几近翻倍

西柚子

智慧云 打造您的商城APP,与5G时代社交电商融为一体

知者如C

GaussDB(DWS)函数不同写法引发的结果差异

华为云开发者联盟

数据库 后端 华为云 华为云开发者联盟 企业号 8 月 PK 榜

Photoshop Elements 2020 for Mac(图形处理工具) v18.0(2020.01.20)激活版

mac

苹果mac Windows软件 Photoshop Elements 2020 ps elements 2020

软件研发的道德情操

阿里技术

研发 软件研发

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