Dojo單元測試框架DOH介紹

來源: InfoQ  發布時間: 2011-09-10 22:10  閱讀: 1327 次  推薦: 0   原文鏈接   [收藏]  

  單元測試的重要性已毋須多言,無論是從保證軟件開發質量,還是從節約軟件后期維護成本來說,單元測試都是最佳實踐。而在敏捷編程領域,隨著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代碼的最完整和最有效的框架。

0
0
 
 
 

文章列表

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()