遊戲項目中的自動化測試和持續集成

        現在,許多遊戲項目要麼跳票嚴重,要不就是發佈時Bug多多。當然,這樣的現象並不僅存於遊戲工業。例如,根據2001Standish集團發表的那份 聲名狼藉的報告“極度混亂”所表述的,70%以上的軟件項目要麼被取消,要麼嚴重的超時和超支。然而,遊戲是軟件開發複雜性的最佳代表,不同技能的人需要 協同工作,這也就是某些人所說的遊戲項目中高風險因素所在。

  軟件項目延期、Bug滿天飛和失敗的原因是多種多樣的,但看起來除了隨產品特性不斷變化之外,測試和品質管理是永恆的問題。以我們的經驗來看,相當多數的遊戲開發工作室完全依靠人工的方式來測試遊戲引擎、開發工具和遊戲代碼,幾乎沒有採用自動化過程測試。很巧,在2002GDC的圓桌會議:遊戲中的純軟件工程,只有18%的與會者表示他們參與的項目採用了自動化測試。

  在2000年,我們的客戶,當時新成立的中間件公司對我們的3D引擎的穩定性和大量的BUG抱怨頻頻,我們第一次想到了自動化測試。直到那時,每當完成一 個新特徵,我們還是依靠人工測試,並且使用這些特徵開發出技術演示供市場部使用。我們在徹底分析了情況後得出以下結論,我們的軟件質量問題主要和我們測試方法有關:

  *人工測試不夠全面和徹底,因爲它僅僅花費了很多時間。 代碼在修改或添加之後,它本應運行預定義的人工測試集來保證修改不會產生新的問題。人工測試花費的時間越來越多,並給開發者帶來挫折感,打擊他們執行測試 的積極性。而且,測試的工作量使得開發者不願意改進或優化現有的代碼。

  *當開發者測試他們自己的代碼時,他們總是不願意(潛意識?)執行最苛刻的測試用例,因此就導致了最有可能出錯之處也是最不可能被全面測試到這樣的情形。

  因此,我們決定採用自動化測試,從開發一個新SDK部件開始。結果是鼓舞人心的,最終我們把它推廣到所有的SDK部件開發中去。測試框架極限編程,由Kent Beck和Martin Fowler總結的一系列方法和經驗,帶來了自動化測試的流行。一般來說,自動化測試指無需用戶干預,用來驗證軟件產品中的功能子集的代碼和數據。它可以是用來測試某個特定類方法(通常稱爲單元測試),也可以是用來測試程序功能性的集成測試(功能測試)。

   爲了促進自動化測試進程,有許多開源代碼的單元測試框架,比如CPPunit(C++專用)或Nunit(.Net專用)。這些測試框架提供了GUI來運行測試集並提供測試結果反饋。根據你的項目,也許需要根據你的遊戲進行一些額外的功能擴展和自定義,例如支持跨平臺。這些測試框架的內容,一個單元測試 對應一個函數,測試類由多個單元測試組成,並且包含一個開始和結束測試的方法(例如載入和卸載一幅地圖)。這些測試類可以放在分離的執行文件中,例如 DLL文件,也可以與主項目在一起。除此之外,測試類應該存放在產品代碼之外的文件中,這樣的話,他們就可以很方便的從版本發佈中移除。

  物理引擎的簡單測試代碼,如果任何一個VTEST條件沒有滿足,那麼測試就失敗。該測什麼?當要決定測試的範圍時,實用第一。一般來說,爲簡單的功能編寫單元測試是沒有意義的,比如常見的getter和setter方法。爲了讓自動化測試物有所值,被測試的代碼至少應該是可能會產生錯誤的,比如,發射一束穿透遊戲場景的光線並且返回它穿過的任何幾何物體的方法(光線測試),然後將返回的結果與編寫測試用例的作者提供的預期結果作比較。

  到底是隻爲類的公用接口編寫測試用例(黑盒測試)還是要兼顧類的私有成員(白盒測試),是一個有爭議的問題。通常來說,黑盒測試比白盒測試粗糙,它們只能檢查一個操作的最終 結果,不能檢查內部中間狀態,它們對於被修改的測試代碼比較遲鈍。剛纔提到的光線測試功能可能被全部重寫(比如原先的版本運行效率不夠),但是它返回的結果沒有變化。這時,白盒測試用例就需要跟着重寫,然而黑盒測試可以繼續用來檢測代碼修改後,所產生的結果是否與原先一致。

  因此,我們認爲自動化測試中,測試範圍只要包括類的公有成員就夠了,畢竟,類的內部修改比它接口修改要頻繁得多。

 

         迴歸測試

   特別是在遊戲開發領域,大多數情況下,把測試結果和用例編寫者提供的數據手工作比較是不太現實的。例如,檢測與複雜的幾何體碰撞的交點,人工提供相關測試數據幾乎不可能。相反,將測試結果與早期代碼產生的結果數據相比較,被稱爲“迴歸測試”。用例編寫者可以評審參考數據,例如,使用簡化圖形的碰撞物體,如果被證實是正確的,它就可以一直用於測試。這樣,自動化測試可以幫助你確認新代碼產生的結果與原先的一致。

   代碼功能測試會生成非常複雜的輸出數據,比如遊戲的圖形渲染引擎,迴歸測試是唯一可行的自動化測試。以圖形渲染引擎爲例,所有圖形測試以輸出最終平臺相關的圖形文件爲結果。一旦自動化測試開始運行,渲染出來的圖形文件與樣本圖形文件逐一像素的進行比較,如果有差異,那麼測試失敗。爲了減少樣本圖形文件的存佔用,你可以使用圖形快照來進行測試。

  圖形迴歸測試的優勢在於即使是肉眼難以發現的微小差異也不會被漏掉。除非人們對這個場景非常熟悉,否則很難會有人注意到場景中缺失的一個陰影或一個物體或者某個光源的R值與B值被錯換了。而回歸測試就不會放過任何一個這樣的錯誤。

   必須注意到,任何情況下,迴歸測試的樣本數據都是自動生成的。樣本數據也許是平臺相關,特別涉及到渲染輸出的時候,因此,它也許要被生成多次,即使是這樣,當渲染通道發生變化導致生成的圖形文件有所改變,樣本數據也要重新生成。爲了不打擊開發者編寫回歸測試的積極性,要做到只需點擊框架用戶界面上一個按鈕就可以重新生成新的參考數據。

 

  如何把所有的整合在一起

  包括遊戲在內的所有應用,完整的測試集合包括單元測試和迴歸測試。單元測試適合於測試底層功能性、基礎庫文件和平臺類庫。上層的各種功能特徵集成測試可以使用迴歸測試。根據結果,你可以有選擇的重構或優化你的邏輯或引擎代碼,迴歸測試一旦失敗,你會馬上發現出問題的地方,單元測試失敗可以讓你精確的定位出錯之處。

  知道代碼被你編寫的自動化測試覆蓋得範圍是非常有好處的,你可以使用代碼覆蓋率調查工具(BullseyeCoverage/AQtime)。代碼覆蓋率分析會告訴你,你的代碼哪些被調用,也可以提示你測試集合中的疏漏之處。測試覆蓋率應該是多少,無法精確定量,儘管它取決於被測試的代碼。細小的方法無需測試,調試用的函數也不必測試。並且,幾乎所有的項 目都會包括一些“死”代碼,也就是不會被調用到的代碼,那麼,這些代碼自然也不用測試。總而言之,現實中,我們見過的使用自動化測試的遊戲和中間件項目中測試覆蓋率大致是55%到70%

 

  編寫友好的測試代碼

  必須承認,並不是所有的代碼都能使用自動化測試。以單元測試爲例,嚴格的面向對象,良好的類和函數模塊化封裝設計可以大大提高它的測試效率。類的接口越多,爲它編寫的單元測試就越多。同樣,過多的使用C++的友元也會增加編寫的難度,甚至無法爲該類編寫(黑盒)單元測試用例。

  在寫代碼的時候,要時刻牢記保持良好的測試性。在開發過程中,就會變成可行但是單調乏味的工作,有時候它需要很好的結構性。要在遊戲開發中使用,以下幾點必須牢記:

  *所有的迴歸測試都取決於明確的行爲。比如,使用隨計算法的尋徑系統可以提供一個初始化隨機種子的公共方法使得角色的行動決策更復雜多變。這個方法在隨後的測試中可以被用來確保角色始終選取同樣的路徑。

  *同樣,迴歸測試中要避免與幀數相關的情況;否則,有真實物理特性的物體或渲染輸出也許會和以前的數據不同,特別是當結果來自不同的機器或者不同的編譯條件(debug 和release)。在測試時,使用恆定的虛擬幀數就可以避免這樣的問題。

  * 嚴重依賴於用戶輸入的軟件很難測試,比如遊戲內置的編輯系統或者遊戲工具。這樣的話,把UI 和邏輯代碼嚴格的區分開會有所幫助。在我們的遊戲工具裏, UI界面裏每個用戶動作會執行一條或多條簡單的腳本指令。每條腳本指令可以很明確的重現用戶的原意。這樣,測試用例可以簡單的執行這些指令並且與樣本數據 作比較就可以(比如導出地形文件)。

  也可以使用GUI捕捉工具來測試UI,但我們發現這並不是個好辦法。UI會經常改變,哪怕一個按鈕僅僅移動幾個像素也會使捕捉軟件失效,GUI捕捉工具也許會幫倒忙。

  關於測試的疑問:我們真的可以節省時間麼?

  多數情況下,一個開發團隊想要在開發過程中使用自動化測試,大多數成員都會對它抱以質疑的態度。畢竟,與其花這點時間寫測試用例,還不如去寫邏輯和引擎 的代碼。根據我們在遊戲和其他領域的工作中使用自動化測試的經驗來看,編寫測試代碼會額外增加30%工作量。初看,在時間和資金上這也許是很大的開銷,然而,你要意識到這樣做,省去了人工測試所花費的時間。

  自動化測試可以看作在開發前期投入,在開發過程中贏利。大多數的代碼修改,包括Bug修改,都可能會引入更多問題導致程序宕機。所以,理論上說,一旦代碼有所改變,就必須測試所有可能被影響的代碼。自動化測試無需人工干預就可以完成,它們縮短了開發過程。而且由於自動化測試可以簡單快速的發現修改的代碼是否能有效地運行,因此也就鼓勵開發者優化和改進現有的代碼。

  對我們來說,自動化測試幫助開發者編寫更穩定和可靠的代碼。哪怕是一開始對它抱有懷疑態度的開發成員也欣賞它所提供早期反饋的特性,在開發過程中,它也可以更早的 發現Bug。開發者的工作壓力和負荷隨着項目的開展日益加大,儘早的發現和解決Bug也可以避免給開發關鍵時期帶來額外的壓力。

  在開發Vision引擎的時候,我們收集了一些數據來研究爲提高代碼穩定性而實施自動化測試的有效性。2001年早期,全部依靠人工測試的引擎第一個 release版本開發完成,儘管我們已經進行了很全面的測試,但是每個月,我們的在線技術支持數據庫依然會收到上百個來自客戶的Bug報告。2001年 9月,我們對已有的引擎功能和新增的特徵實施自動化測試。這樣,即使我們現在的工作量很大,開發進展也很正常,每月Bug報告的數量銳減(現在大概是5到10個)。

  必須強調,這些圖表只是反映了單元測試用例數量和每月Bug數量兩者之間的相互關係,不能將它理解爲必然的因果關係。當然,從2001年到2004年,我們在如何編寫健壯的代碼上學到了很多,在這段時間內,開發團隊的人數變動頻頻,但是,這些明顯的差異足以說明穩定性的提升部分歸功於使用了自動化測試。

    

     遊戲中自動化測試的侷限性

  儘管使用自動化測試對於遊戲開發來說獲益匪淺,但是也有其侷限之處。顯然,很難用它來測試遊戲的平衡性,也不太可能用它來測試遊戲性和畫面的美觀性。在這幾年裏,我們總結了一些編寫自動化測試的要點,重點如下:

  *測試最重要的模塊(比如,最複雜和最常用的)。對那些最有可能出問題或者不會破壞原先設計的重構任務進行自動化測試,性價比最高。

  *當上層功能性測試難以進行的時候,把精力集中在不同的子系統。例如,你也許不能通過自動化測試來驗證AI系統是否正常工作,但你可以測試當一個怪獸的體力低於一定數值的時候,它是否會“投降”。

  *用壓力測試來驗證你的代碼的強壯性。如果你的遊戲在極端條件下運行的很好,比如,每秒有2000個怪獸出生和死亡,一個場景中同時放入500個有真實物理特性的物體,一幅地圖輪流載入200次,那麼玩家作一些異常操作時,宕機的可能性就會小很多。

  *在修改Bug前,也爲它編寫測試用例。這樣的話,可以確保這些Bug在今後的版本中不會重現。

  *迴歸測試。例如,圖像或狀態比較的話,使用指定的測試場景要比使用產品地圖更容易維護。如果你認爲測試用產品數據可能會經常變動,那麼你最好使用小的測試場景。否則,不斷的生成新的參考數據會使得開發團隊產生疲倦和厭煩的情緒。

  * 測試用例越簡單越好,不要奢望有非常大的覆蓋面。搭建一個易維護和可擴展的自動化測試是一個長期的任務。一般來說,“底層”代碼,例如算術、碰撞檢測和渲染,更容易進行自動化測試,對於遊戲性和完整的遊戲測試來說,還是需要經過QA人員親自測試。當然,QA部門的注意力也要從技術轉移到與遊戲性相關的問題上去。“到A房間後,因爲通風口前面的箱子太高了,所以出不去”這樣的報告就會取代“當我的角色轉動的時候,屏幕上出現了很多扭曲的三角面”。

  持續集成

  在一個複雜的軟件項目中引入自動化測試,你會發覺運行它也需要一定的時間,我們看到的一些項目甚至需要幾個小時。如果讓開發者在他們的開發用機上運行的話,測試會完全佔用他們的機器,影響工作,那麼結果就是他們不去運行測試用例,很顯然,沒有被運行的用例是沒有任何價值的。

  解決方法就是搭建一臺或多臺專用的自動化測試機。它同時還運行版本管理軟件(Subversion/CVS/Perforce),一旦發現提交了新代碼,那麼代碼就會被Check out並編譯,測試用例也會自動運行。最後,系統會將測試結果報告以email的形式自動發送給最近提交代碼的開發者。

  完全自動化、重複的 build和測試過程,這種過程每天運行多次,在極限編程中,我們把它稱爲:持續集成。爲了更好的實行持續集成,像 Cruise Control或者Anthill這樣的開源代碼工具可以將版本管理軟件和自動build工具,例如ANT,整合起來。使用這樣的工具, 可以很輕鬆的搭建適合自己的持續集成系統

  我們發現搭建專用持續集成服務器使得開發過程變得更順暢,開發者可以更專注於自己的工作。與此同時,測試可以被很好的運行,一旦提交了錯誤的代碼,持續集成系統會自動通知開發者和項目經理,因此開發者也可不必爲此分心。自動化,自動化!

  自動化測試和持續集成的使用爲我們在遊戲和工具的開發上帶來了極大的收益。例如,持續集成服務器根據Wiki中的變化,每天自動生成CHF (windows幫助文件文件。而且,使用ANT和CruiseControl來製作軟件自動分發包會非常容易。這樣一來,用最新的代碼(或最新的 tag)創建一個完整的分發包就是舉手之勞。

  自動化過程中的自動化測試執行,例如測試框架中的常規單元和迴歸測試,他們不是用來檢查錯誤,而是用在相同環境下得到測試結果來衡量和比較引擎的性能(系統配置的結果以 XML文件格式存放在版本管理軟件系統上)。如果當前的結果比參考結果差很多,那麼測試失敗,反之,它就成爲了新的參考結果。

  性能測試是一種特殊的迴歸測試。當引擎代碼被重構,我們通過它來確保修改不會降低引擎原有的性能。這樣,就迫使我們時刻關注代碼的運行效率和對代碼的優化工作,可以避免遇到在實際運行中,速度突然變慢的現象發生。

  結論

  根據我們的經驗,使用自動化測試和持續集成可以使開發團隊工作更有效而開發出更好、穩定、簡單的軟件。而且,減少人工測試也可以減少開發團隊的壓力和工作負荷,可以在開發過程中儘早的發現Bug。

  當然,自動化測試不會使你的遊戲想當然成爲暢銷品。但毋庸置疑,它會使各類開發人員甚至玩家活得更自在。

 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章