Dojo单元测试框架DOH介绍

  单元测试的重要性已毋须多言,无论是从保证软件开发质量,还是从节约软件后期维护成本来说,单元测试都是最佳实践。而在敏捷编程领域,随着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模块的测试结果:

Dojo单元测试框架DOH介绍_第1张图片

  左边的是测试用例列表,可以看到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文件夹,用来存放测试用例:

Dojo单元测试框架DOH介绍_第2张图片

  之后在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 :

Dojo单元测试框架DOH介绍_第3张图片

  与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包含了两组测试用例:

Dojo单元测试框架DOH介绍_第4张图片

  手动的给/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代码的最完整和最有效的框架。

你可能感兴趣的:(单元测试,Dojo,DOH)