前言
項目中需要用到前端自動化測試,自己被當作一個探針研究了下目前用的比較多的web自動化測試工具。一開始研究的是的selenium,但由於項目使用了大量的dijit控件,寫起testCase來很費勁;最主要的是selenium有嚴重的瀏覽器兼容性問題,於是徹底放棄,改投doh門下,畢竟是dojo他爸爸開發的跟dojo繼承起來非常方便。這幾篇主要介紹doh、doh/Robot以及將doh與selenium結合進而與CI工具集成起來。
一、DOH 是什么
DOH 是 Dojo Objective Harness 的簡稱,是 Dojo 在 0.9 版本之后新增的單元測試工具。隨着 Javascript 代碼的數量和復雜度的增加,Web 前端開發者也需要一個 Javascript 代碼的單元測試框架來保證自己寫出來的 Javascript 代碼是健壯的。所以,DOH 是 Web 前端開發者對 JUnit 的回應。DOH有如下特點:
- 提供用戶界面:JUnit中的紅條測試失敗、綠條測試通過,大家都已經很熟悉了,DOH也有類似的用戶界面,用戶在測試時更加一目了然;
- 平台無關:DOH並不依賴某種瀏覽器平台,甚至不依賴於瀏覽器;用戶可以根據自己的需要在命令行進行Javascript的自動化單元測試;
- 支持Ajax:Ajax編程在Web前端開發中是必不可少的一環,DOH最有價值的一個特性就是支持Ajax的測試;
- 不只適合與於Dojo,可用於任何JavaScript程序的單元測試。
下載dojo源碼包,解壓后可以看到以下目錄:
二、DOH第一次親密接觸
部署到服務器后,我們訪問runner.html
與JUnit類似,DOH也為我們提供了一個類似的界面,左側表示運行的測試案例,右側是測試日志,最上方的進度條表示的是本次運行中的已執行案例,同樣綠色表示成功,紅色表示失敗。默認情況下會加載“dojo/tests/module.js”文件,該文件的作用就是像runner.html中加載dojo所有核心模塊的測試案例。
如果想要單獨加載某一模塊的測試案例,需要用到test參數指向該模塊:
http://<server>/util/doh/runner.html?test=dojo/tests/fx
我們可以吧測試案例放到自己的項目目錄下,通過test參數指向自定義測試模塊,這時需要用到paths參數:
util/doh/runner.html?paths=org/myorg,../../../mycode/org/myorg&test=org/myorg/mymodule/tests/alltests
paths參數中逗號前后的值相當於dojoConfig定義packages對象時的name和location
同樣在path中我們可以定義多個模塊, 模塊之間用“;”分隔開來
util/doh/runner.html?paths=org/myorg,../../../mycode/org/myorg;com/mycom,../../../x/com/mycom&test=com/mycom/tests
doh中測試模塊要么是一個用來請求多個測試文件的文件,要么是一個使用doh.register方法注冊時測試案例,或者兩者皆有。
define([ "my/test/widget/Foo0", "my/test/widget/Foo1", "my/test/widget/Foo2" ]);
或者
define(["doh/runner"], function(doh){ doh.register("MyTests", [ function assertTrueTest(){ doh.assertTrue(true); doh.assertTrue(1); doh.assertTrue(!false); }, { name: "thingerTest", setUp: function(){ this.thingerToTest = new Thinger(); this.thingerToTest.doStuffToInit(); }, runTest: function(){ doh.assertEqual("blah", this.thingerToTest.blahProp); doh.assertFalse(this.thingerToTest.falseProp); // ... }, tearDown: function(){ } }, // ... ]); });
三、測試用例
doh為我們提供了以下斷言函數:
doh.assertTrue(boolean) Note: This function is aliased to doh.t(); doh.assertFalse(boolean) Note: This function is aliased to doh.f(); doh.assertEqual(obj1, obj2) Note: This function is aliased to doh.is(); doh.assertNotEqual(obj1, obj2) Note: This function is aliased to doh.isNot();
Junit中,編寫一個測試用例我們需要繼承Junit類,同樣在doh中也有自己的規則來將一段代碼,包裝成doh可識別的測試案例。
doh中主要提供了以下方法(最常用的就是doh.register):
doh.register(...) An almost 'magical' function. The doh.register() method accepts the function signatures of any of the other registration functions and determines the correct underlying function (listed below) to dispatch registration to. It's the function you'll most commonly use for registering Unit Tests.
doh.registerTest(group, testFuncOrObj) This function registers a test as a member of the group 'group', and the test can either be a simple function definition or a 'Test Fixture', which is an object that defines the run requirements of the test.
doh.registerTests(group, testFuncOrObjArr) This function registers an array of tests as a member of the group 'group'. The contents of the array of tests can be an array of simple test functions or an array of 'test fixtures', or a mix of them.
doh.registerTestNs(group, obj) This function registers an object comprised of functions as a member of the group 'group'. Note that this function will only add in non-private (functions without an _ at the beginning of the name), as a test function. If you'd like to use fixtures (setUp(), tearDown(), and runTest()), please use doh.register(), doh.registerTest() or doh.registerTests().
doh.registerTestUrl(url) This function registers a URL as a location to load tests from. The URL is used to populate the contents of an iframe, and usually refers to an HTML page that boot-loads D.O.H. internally for running tests in a segmented iframe. A good example showing this is the dojo/tests/fx.html. It loads dojo, doh, and then on dojo load completion calls doh.registerTests(). The D.O.H. instance in the iframe will proxy back the results of the test run to the primary D.O.H. instance.
上面提到的dojo/test/fx模塊中我們可以看到該文件中主要定義了兩個TestSuite:
define(["doh/main", "require"], function(doh, require){ if(doh.isBrowser){ doh.register("tests.fx", require.toUrl("./fx.html"), 30000); doh.register("tests.NodeList-fx", require.toUrl("./NodeList-fx.html"), 30000); } });
對應於runner.html中
打開該目錄下的fx.html文件,我們可以看到該文件中定義了一系列TestCase
doh中有兩種測試結構:
1、Simple Tests 將一個單獨的函數放到doh.register參數testCase數組里
同步形式:
function mySimpleTest(doh){ doh.assertTrue(true); }
異步形式:
function mySimpleAsyncTest(doh){ var deferred = new doh.Deferred(); setTimeout(deferred.getTestCallback(function(){ doh.assertTrue(true); }), 100); return deferred; }
2、Test Fixture
同步形式:
{ name: "thingerTest", setUp: function(){ // Setup to do before runTest.//類似於JUnit中的@beforeTest this.thingerToTest = new Thinger(); this.thingerToTest.doStuffToInit(); }, runTest: function(){ // Our test function to run.//類似於JUnit中的@Test doh.assertEqual("blah", this.thingerToTest.blahProp); doh.assertFalse(this.thingerToTest.falseProp); // ... }, tearDown: function(){ // cleanup to do after runTest.//類似於JUnit中的@afterTest }, timeout: 3000 // 3 second timeout.//測試運行時間,超過改時間會報錯 }
異步形式:
{ name: "thingerTest", setUp: function(){ // Setup to do before runTest. this.thingerToTest = new Thinger(); this.thingerToTest.doStuffToInit(); }, runTest: function(){ // Our test function to run. var deferred = new doh.Deferred(); setTimeout(deferred.getTestCallback(function(){ doh.assertEqual("blah", this.thingerToTest.blahProp); doh.assertFalse(this.thingerToTest.falseProp); }), 100); return deferred; }, tearDown: function(){ // cleanup to do after runTest. }, timeout: 3000 // 3 second timeout. }
參考資料:
http://www.infoq.com/cn/articles/dojo-unit-test-DOH/
http://tech.ddvip.com/2010-04/1271148731150772_2.html
