溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Flash持續(xù)集成自動化單元測試

發(fā)布時間:2020-06-04 00:15:25 來源:網(wǎng)絡 閱讀:1550 作者:百度技術 欄目:軟件技術

本文關注于宏觀上的CI和單元測試技術,某些技術上的具體細節(jié)會略過,更多細節(jié)請參考最后部分的“參考資料”及文中的鏈接。

作者:杜明坦

本文包括:持續(xù)集成、單元測試、Mock技術、Case選取策略和示例等五部分

持續(xù)集成(CI)
CI是一種實踐,旨在緩和和穩(wěn)固軟件的構建過程,能夠應對如下挑戰(zhàn):

  • 軟件構建自動化
  • 持續(xù)自動的構建檢查
  • 持續(xù)自動的構建測試(本篇文章的重點所在)
  • 構建生成后續(xù)過程的自動化
  • Flash持續(xù)集成自動化單元測試

詳情見:
http://www.javaworld.com/javaworld/jw-12-2008/jw-12-hudson-ci.html


hudson及搭建
一個非常流行的CI工具,易于安裝及配置,學習成本低,本篇中hudson安裝及配置在windows平臺進行。

安裝JDK

從www.java.com下載直接安裝,然后設置java相關的環(huán)境變量:

JAVA_HOME:jdk安裝目錄,例如:c:\java\jdk1.7.0
PATH:使得系統(tǒng)在任何路徑下可以識別java命令,設為:%JAVA_HOME%/bin;%JAVA_HOME%/jre/bin
CLASSPATH:為java加載類(class or lib)路徑,只有類在classpath中,java命令才能識別,設為:.;%JAVA_HOME%/lib/dt.jar;%JAVA_HOME%/lib/tools.jar (要加.表示當前路徑)
安裝ANT
從http://ant.apache.org/下載ant1.8.2
解壓到任意目錄,設置環(huán)境變量ANT_HOME指向ant主目錄
設置環(huán)境變量ANT_OPTS,值為-XX:MaxPermSize=128M -Xms128M -Xmx512M分配更大的內存空間
安裝Hudson
首先下載hudson.war http://www.hudson-ci.org/
其次通過 java -jar hudson.war命令運行
由于本身內置http服務,在瀏覽器中打開http://localhost:8080查看hudson是否運行
持續(xù)運行方法
簡明使用方法
添加節(jié)點并進行設置

可以理解為一個項目,以下為admaker為例。 需要更多節(jié)點,請點擊”New Node

Flash持續(xù)集成自動化單元測試 

節(jié)點設置,主要設置”Remote FS root”和“Launch Method”即可。

Flash持續(xù)集成自動化單元測試

添加job并進行設置
可以理解為創(chuàng)建項目的一個任務,例子為auto,使用下圖選項即可:

Flash持續(xù)集成自動化單元測試

集成flexunit/pmd/cpd
hudnson通過ant進行自動化編譯,把編譯的結果設置為hudson的讀取源,運行原理如下:

Flash持續(xù)集成自動化單元測試 

自動化編譯設置

拷貝sdk\x.x.x\ant\lib下的flexTasks.jar至ant\lib目錄下,并在build.xml中通過以下指令指定:

  1. <taskdef resource="flexTasks.tasks" classpath="${FLEX_HOME}/ant/lib/flexTasks.jar" /> 


單元測試所需文件設置

從https://github.com/flexunit/flexunit下載源代碼,通過ant進行自動化編譯,生成類似flexUnitTasks-4.1.0-x.jar的文件,拷貝到 項目所在的libs文件夾,并在build.xml中通過以下指令指定:

  1. <taskdef resource="flexUnitTasks.tasks"> 
  2. <classpath>  
  3. <fileset dir="${lib.loc}">  
  4. <include name="flexUnitTasks*.jar" />  
  5. </fileset>  
  6. </classpath>  
  7. </taskdef>


PMD/CPD

flexpmd,主要用來提升Flex/AS3源文件中的代碼質量并且檢測常見的不好的代碼實踐,比如無用的代碼,效率低的代碼片段,過于復雜的代碼等等這些都是FlexPMD檢測并報告的對象。

flexcpd,F(xiàn)lex/as3源文件中的代碼重復率檢查工具。 下載利用ant編譯所需要的文件,最新版為1.2:http://opensource.adobe.com/wiki/display/flexpmd/Downloads

配置(flexpmd):http://opensource.adobe.com/wiki/display/flexpmd/How+to+invoke+FlexPMD

配置(flexcpd):http://opensource.adobe.com/wiki/display/flexpmd/FlexCPD

條件編譯參數(shù)的方法

在flex-config.xml文件中的<compile></compile>中添加以下內容:

  1. <define>  
  2. <name>CONFIG::debug</name>  
  3. <value>false</value>  
  4. </define>  
  5. <define>  
  6. <name>CONFIG::embed</name>  
  7. <value>false</value>  
  8. </define>  
  9. <define>  
  10. <name>CONFIG::edit</name>  
  11. <value>true</value>  
  12. </define> 


構建
點擊“Build Now”即可看到相應結果

Flash持續(xù)集成自動化單元測試

 

windows下默認端口1024被占用問題
中的屬性port是指定socket server的端口號,而CIListener中的默認端口號是1024,此時需要做的是:

修改CIListener.as和VisualDebuggerListener.mxml中的默認端口號為9900,在flexunit項目目錄中執(zhí)行ant編譯,在FlexUnit4Test\libs中找到flexunit-cilistener-41.0-1-4.5.0.0.swc文件,放到你的項目相應的地方即可,此時要注意顯示指定port=”9900″。

為了不顯示指定端口號9900,需在TestRunConfiguration.java中修改默認端口號為9900,在flex項目目錄中執(zhí)行ant編譯,在FlexUnit4Test\libs\build中找到flexUnitTasks-4.1.0-1.jar文件,放到你的項目的相應的位置即可。 建議把完成以上兩個步驟的操作

單元測試
flexunit4
進化史

as2unit 2003

flex1.0 flexunit

flex2.0 flexunit.9 as3—> as3flexunit(google code)—–>back to adobe

dpUInt—〉Fluint

flexunit4 2009

框架原理圖

Flash持續(xù)集成自動化單元測試

 

使用方法

在項目上點擊右鍵,添加test case 或test suite(為case套裝,包含n個case),添加相應的文件,flash builder會為你自動創(chuàng)建測試用主類FlexUnitApplication.as
在項目上點擊右鍵“執(zhí)行單元測試”或通過單元測試面板執(zhí)行,選擇相應的test case 或者test suite運行即可。
一般的結構如下,把AM2Test作為唯一的入口即可:

Flash持續(xù)集成自動化單元測試

基礎知識
testMethod,testCase,testSuite

結構圖如下:

Flash持續(xù)集成自動化單元測試

testMethod

最小的測試單元,顧名思義,針對類的方法的測試,使用[Test]元標簽,示例如下:

  1. [Test] 
  2. public function testUpload(){}; 


策略:

一般為要至少為類中的每個方法寫一個testMethod
對于一個方法,由于不同的條件邏輯可能會產(chǎn)生不同的結果,針對每一個結果寫一個testMethod
針對沒一個方法,寫兩個testMethod,針對有效和無效的輸入
TestCase

testMethod的集合,測試多個相關的功能點,一般針對某一個類,其中包含所有的需要測試的testMethod,包含如下特有的元數(shù)據(jù)標簽,可以重復書寫:

  1. [Before] 
  2. [Before Class] 
  3. [After] 
  4. [After Class] 
  5. [Test] 

其中每個[Test]是可以有順序的,通過order屬性指定,形如:

 

  1. [Test(order=1)] 
  2. public function testOrder1(){}; 
  3. [Test(order=2)] 
  4. public function testOrder2(){}; 
  5. TestSuite 

testCase的集合,使用如下元數(shù)據(jù)標簽標記,示例如下:

  1. [Suite] 
  2. [RunWith("org.flexunit.runners.Suite")] 
  3. public class MultiFileUploaderSuite{ 
  4. public var _testUploadList:TestUploadList; 

斷言
斷言用于比較測試目標的結果和預期值,一般格式如下:

assert( "錯誤提示", 預期值, 實際值 );
示例:

result = 1 + 2;
assertEqual( "結果不為3", 3, result );
有兩種使用格式:

hamcrest
開源的匹配器庫,格式如下:

assertThat( value, matcher );
其中,matcher是一個類,有is(),between(),closeTo()等

用法舉例:

assertThat( num1, is( between( num2, num3 ) ) );
自定義matcher:

  1. import org.hamcrest.TypeSafeMatcher; 
  2. import org.hamcrest.Description; 
  3. public class myMatcher exends TypeSafeMatcher{ 
  4. public function myMatcher(){}; 
  5. override public function matchesSafely(item:Object):Boolean {}; 
  6. override public function describeTo(description:Description):void {}; 

更多內容見:http://github.com/drewbourne/hamcrest-as3

異步測試
flexunit4的核心功能,想想flash中的各種異步事件吧!包含兩種格式,事件處理和事件序列(sequence)。

事件處理示例代碼:

  1. [Test(async, timeout=5000)] 
  2. public function testCancel():void{  
  3. var mHandler:Function = Async.asyncHandler( this, cancelHandler, 
  4. 5000, {num:4,type:"m"}, timeoutHandler ); 
  5.  
  6. _uploader.addEventListener( UploaderEvent.EVENT_FILE_ALL_MEMEORY, 
  7. mHandler );  
  8.  
  9. _uploader.loadtoMemory();  

如示例所示:

async:必須存在,說明是異步測試
timeout=5000, 在5000毫秒內測試未完成,默認超時處理
mHandle是異步處理對象,包含5個參數(shù),意義如下:

this:針對哪個TestCase,這里即為this
cancelHandler,接收到UploaderEvent.EVENT_FILE_ALL_MEMORY事件后觸發(fā)的事件對象
5000,超時處理觸發(fā)的最大時間
{num:4,type:”m”},傳遞給cancelHandler對象的參數(shù)
timeoutHandler,超時處理對象,5000ms后未觸發(fā)cancelHandler對象觸發(fā)
更多內容見:更多內容見:http://docs.flexunit.org/index.php?title=Writing_an_Async_setup#Approach

事件序列,是一個很實用的使用格式,例如測試一個文件上傳過程中取消的功能,會觸發(fā)的事件有上傳,取消成功,取消失敗等,這個時候假如用事件處理的格式書寫的話,會涉及到n個處理函數(shù),很復雜,而用sequence呢,顯然一目了然。

事件序列示例代碼:

  1. [Test( async )] 
  2. public function shouldCompleteTimerSequence():void { 
  3. var timer:Timer = new Timer( TIMEOUT ); 
  4. var sequence:SequenceRunner = new SequenceRunner( this ); 
  5. sequence.addStep( new SequenceCaller( timer, timer.start ) ); 
  6. sequence.addStep( new SequenceWaiter( timer, TimerEvent.TIMER, TIMEOUT2 ) ); 
  7. sequence.addStep( new SequenceWaiter( timer, TimerEvent.TIMER, TIMEOUT2 ) ); 
  8. sequence.addStep( new SequenceWaiter( timer, TimerEvent.TIMER, TIMEOUT2 ) ); 
  9. sequence.addStep( new SequenceCaller( timer, timer.stop ); 
  10. sequence.addAssertHandler( handleSequenceComplete, null ); 
  11. sequence.run(); 

如示例所示,執(zhí)行步驟如下:

開始一個定時器
循環(huán)執(zhí)行三次
停止定時器
執(zhí)行斷言
更多內容見:更多內容見:http://docs.flexunit.org/index.php?title=Fluint_Sequences

Rules
類似于[Before]和[After]的元數(shù)據(jù)標簽,提供更多高級功能,定義每個test運行前后的規(guī)則,在每個test之前和之后運行,結構如下:

-BeforeClasses
-Rules
-Befores
-Test
-Afters
-Rules (the same ones as above)
-AfterClasses
UIImpersonator
借鑒Fluint框架的測試內容的顯示對象,通過它你可以把可視化的內容暫時在舞臺上,其實個人認為實用性不大,和自動化的思想有違背。在純as3項目中暫時無法顯示,不過可以利用這個技巧實現(xiàn):https://gist.github.com/1094408

更多內容見:http://docs.flexunit.org/index.php?title=UIImpersonator

Runner和自定義Runner
規(guī)定了Test Method、Test Case和Test Suite運行時的行為,兼容性好,能很好的運行flexunit1和fluint框架的測試內容。有不同責任的Runner,最常用的就是Suite。當測試單元運行的時候,Cla***equest對象會根據(jù)每個Test Case和Test Suite的內容進行包裝成為IRequest對象,并為此對象構建相應的Runner,F(xiàn)lexUnitCore.run( Cla***equest)會執(zhí)行所有的IRequest對象,觸發(fā)IRequest的run()方法,執(zhí)行相應的Runner.run()。系統(tǒng)自定義的Runner有:

  1. IgnoredCla***unner 
  2. Suite 
  3. TheoryBlockRunner 
  4. SuiteMethod 
  5. FlexUnit1Cla***unner 
  6. Fluint1Cla***unner 
  7. BlockFlexUnit4Cla***unner 
  8. ParentRunner 

要自定義Runner,可以實現(xiàn)以下方法:

  1. import org.flexunit.runner.IDescription; 
  2. import org.flexunit.runner.IRunner; 
  3. import org.flexunit.runner.notification.IRunNotifier; 
  4. import org.flexunit.token.IAsyncTestToken; 
  5. public class CustomRunner implements IRunner { 
  6. public function CustomRunner() {} 
  7. public function run(notifier:IRunNotifier, 
  8. previousToken:IAsyncTestToken):void {} 
  9. public function get description():IDescription {} 
  10. public function pleaseStop():void {} 

更多內容見:http://docs.flexunit.org/index.php?title=Custom_Runners

UIListener
Runner的監(jiān)聽對象,對監(jiān)聽內容進行圖形化表示,過程如下:

  1. import sampleSuite.SampleSuite; 
  2. import org.flexunit.listeners.UIListener; 
  3. import org.flexunit.runner.FlexUnitCore; 
  4. private var core:FlexUnitCore; 
  5. public function runMe():void { 
  6. core = new FlexUnitCore(); 
  7. core.addListener( new UIListener() ); 
  8. core.run( sampleSuite.SampleSuite ); 
  9. CIListener 

Runner的監(jiān)聽對象,不需要進行圖形化的表示,說要做的就是把監(jiān)聽到的內容通過Socket發(fā)送給服務端,把內容寫到磁盤,供Hudson讀取處理,過程如下:

core.addListener(new CIListener());
core.run( sampleSuite.SampleSuite )}
Mockolate
原理
對象的一個代理對象,包含原對象的所有公共方法和屬性,mock對象的父類為原對象類,這樣就可以在使用原對象的地方使用mock對象。

由于mock技術是一門獨立的技術,在此不作詳述,請參考:http://www.mockolate.org/

使用
自定義Runner,自定義Rule,以及Mock標簽:

  1. [RunWith("mockolate.runner.MockolateRunner")] 
  2. public class TestUploader{  
  3.  
  4. [Rule] 
  5. public var mocks:MockolateRule = new MockolateRule(); 
  6.  
  7. [Mock(type="strict")] 
  8. public var fileReference:FileReference; 
  9. [Mock(type="strict")] 
  10. public var fileReferenceList:FileReferenceList;  
  11.  

如上所示,在運行這個TestCase的時候使用MockolateRunner,自定義MockoateRule,通過Mock標簽指定要mock的類,并指定為strict模式。

接下來做的是在TestMethod中使用它們:
 

  1. var frl:FileReferenceList = strict( FileReferenceList );  
  2. mock( frl ).getter( "fileList").returns( [] );  
  3. mock( frl ).method("toString").returns( "FileReferenceList" ); 
  4. mock( frl ).method( "browse" ).args( Array ).dispatches( new Event( Event.SELECT, false, false ) );  
  5.  
  6. _uploader.fileReferenceList = frl

如上所示,
getter方法fileList的返回為一個[],

toString()方法返回“FileFeferenceList”,

調用browse()方法時,指定類型為Array的參數(shù),并派發(fā)SELECT事件。

最后,通過_uploader.fileReferenceList = fr1,把Mock對象傳遞給_uploader對象。

Case選擇策略
針對接口,但不針對getter/setter方法
核心功能點,比如上傳模塊中的上傳、中斷等功能
邏輯復雜的功能點,if-else、switch-case等
針對功能點,可能組合各函數(shù),在時間有限的情況下,非核心功能點可以省去
根據(jù)bug書寫case,復現(xiàn)步驟,并驗證之
不針對樣式和展現(xiàn)等相關的功能點,例如,“當文件名為20個中文的時候,上傳列表顯示錯亂”這樣的問題,應完全由QA保證,屬于非單測范圍。
在單測開始之前,開發(fā)人員應和QA溝通確認哪些case是由QA保證,哪些case是由開發(fā)人員單測保證
示例展示
以多文件上傳核心類Uploader為例,要想測試此類必須要使用mock技術,因為FileReference的data為只讀屬性

根據(jù)選取策略,最終鎖定在來Uploader類的核心方法,并建立如下的case:

  1. testUploadedToMemory() 
  2. testUploadedALLComplete() 
  3. testUploadedALLError() 
  4. testCancel() 
  5. testResume() 
  6. testDelete() 

每個case都會對FileReference、FileReferenceList和用于和AMF后端通訊的核心類AMFPHP進行mock,

testCancel()的示例代碼如下:
 

  1. /** 
  2. * 取消上傳,上傳完成2個后,cancel,驗證以上傳的ID列表的值是否為2 
  3. *  
  4. */  
  5. [Test(async, order=4)] 
  6. public function testCancel():void{ 
  7. _count = 0
  8. mockFileReference(); 
  9. mockAMFPHP( true );  
  10. var mHandler:Function = Async.asyncHandler( this, cancelHandler, 5000, {num:4,type:"m"}, 
  11. timeoutHandler ); 
  12. _uploader.addEventListener( UploaderEvent.EVENT_FILE_ALL_MEMEORY, mHandler ); 
  13. _uploader.addFileType( "test" ); 
  14. _uploader.browse();  
  15.  
  16. FileReferenceHeFileReferenceList的mock代碼: 
  17. /** 
  18. * mock reference相關的類  
  19. *  
  20. */  
  21. private function mockFileReference():void{  
  22. var frl:FileReferenceList = strict( FileReferenceList );  
  23. var num:int = 4
  24. var arr:Array = []; 
  25. for( var i:int; i &lt; num; i++ ){ 
  26. var fr:FileReference = strict( FileReference );  
  27. mock( fr ).getter( &quot;data&quot; ).returns( _ba ); 
  28. mock( fr ).getter( &quot;type&quot; ).returns( &quot;.jpg&quot; ); 
  29. mock( fr ).getter( &quot;size&quot; ).returns( 10000 ); 
  30. mock( fr ).getter( &quot;name&quot; ).returns( &quot;fr&quot; ); 
  31. mock( fr ).method( &quot;toString&quot; ).returns( &quot;FileReference&quot; );  
  32. mock( fr ).method(&quot;load&quot;)  
  33. .dispatches( new Event( Event.COMPLETE, false,false ) ) 
  34. .dispatches( new IOErrorEvent( IOErrorEvent.IO_ERROR, false, false ) ); 
  35. arr.push( fr );  
  36. }  
  37.  
  38. mock( frl ).getter( &quot;fileList&quot;).returns( arr );  
  39. mock( frl ).method(&quot;toString&quot;).returns( &quot;FileReferenceList&quot; ); 
  40. mock( frl ).method( &quot;browse&quot; ).args( Array ) 
  41. .dispatches( new Event( Event.SELECT, false, false ) );  
  42. _uploader.fileReferenceList = frl

事件處理cancelHandler方法如下:

  1. /** 
  2. * 上傳過程中中斷處理 
  3. *  
  4. * @param e 
  5. * @param pd 
  6. *  
  7. */  
  8. private function cancelHandler(e:UploaderEvent, pd:Object):void{ 
  9. var sHandler:Function; 
  10. switch( pd.type ){ 
  11. case "m":  
  12. sHandler = Async.asyncHandler( this, cancelHandler, 5000, {num:4, type:"a"}, timeoutHandler );  
  13. _uploader.addEventListener( UploaderEvent.EVENT_FILE_COMPLETE, sHandler ); 
  14. _uploader.upload(); 
  15. break; 
  16. case "a":  
  17. if( ++_count == 2 ){ 
  18. _uploader.cancel();  
  19. Assert.assertEquals( "cancel失效:", 2, _uploader.uploadedIDAll.length ); 
  20. }  
  21. }  


如上所示:

調用_uploader.browse(),

派發(fā)Event.SELECT事件

Uploader內部把文件內容加載到內存,派發(fā)UploaderEvent.EVENT_FILE_ALL_MEMEORY事件

在CancelHandler中處理UploaderEvent.EVENT_FILE_ALL_MEMEORY事件

假如pd的屬性type為m,進行上傳

通過_count變量判斷上傳了兩次,調用_uploader.cancle()方法

進行Assert,判斷上傳成功的數(shù)組列表的長度是否為2

失敗的話,輸出”cancel失效: ”

參考資料

 

http://docs.flexunit.org/ flexunit 官方教程

http://tutorials.digitalprimates.net/index.htm  flexunit4.1完整教程

https://github.com/flexunit/flexunit flexunit4源代碼

http://mockolate.org mockolate官方教程

http://hudson-ci.org/ hudson官網(wǎng)

http://www.unitedmindset.com/jonbcampos/2010/02/02/run-flex-unit-tests-from-ant/

http://flexunit.digitalprimates.net:8080/view/All/

http://blog.csdn.net/lixuekun820/article/details/5881647

http://www.flexonjava.net/2008/12/flex-3-unable-to-resolve-resource.html

http://macleo.iteye.com/blog/870004

http://stackoverflow.com/questions/3714957/address-already-in-use-jvm-bind

http://forums.adobe.com/community/opensource/flexunit?view=all 

https://gist.github.com/1094408 

      
更多資料,請baidu一下-_- 

 

【本文首發(fā)于:百度泛用戶體驗http://www.baiduux.com/blog/2012/02/06/flash-ci-and-testing/
關注百度技術沙龍
向AI問一下細節(jié)

免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內容。

AI