最近利用些業(yè)余時(shí)間自己編寫(xiě)了一個(gè)小型自動(dòng)化測試框架,在設計過(guò)程中自己也漸漸對自動(dòng)化框架的作用有了些新的認識,希望能和大家分享一下。
其實(shí)設計這個(gè)框架最初的動(dòng)機是來(lái)源于工作中的一個(gè)任務(wù)——同事讓我維護一個(gè)很久以前編寫(xiě)的“自動(dòng)化腳本”,難度不大,只是一串處理和過(guò)程,看懂代碼以后只需要修改個(gè)別邏輯和參數即可。但后來(lái)我想了想,這樣純粹只有過(guò)程的腳本,在開(kāi)發(fā)測試時(shí)用來(lái)當做小工具用不錯,但一旦需要建立穩定的自動(dòng)化測試機制,有大量功能點(diǎn)和測試數據的時(shí)候就會(huì )顯得力不從心。一個(gè)功能點(diǎn)對應一個(gè)腳本,新增功能點(diǎn)甚至測試數據都需要對應增加腳本,開(kāi)發(fā)維護的成本則會(huì )非常高。
后來(lái)我自己嘗試去做了一個(gè)小型的自動(dòng)化測試框架,雖然花費了不少時(shí)間才實(shí)現了原來(lái)腳本的內容,但是磨刀不誤砍柴工,有了框架,接下來(lái)新增功能點(diǎn)的開(kāi)發(fā)工作量大大減輕。自己在設計該框架時(shí)也基本上是摸著(zhù)石頭過(guò)河,一邊思考自動(dòng)化框架究竟需要做什么,一邊也參考一些開(kāi)源自動(dòng)化框架例如Ruby Watir的設計方式,以下便是我總結的一些經(jīng)驗和心得。
一、測試腳本與測試框架脫離
我開(kāi)頭提到的那個(gè)“測試腳本”,從程序啟動(dòng),測試動(dòng)作執行,測試結果反饋都一手包干,例如對于A(yíng)1和A2兩個(gè)相似的功能點(diǎn),其測試代碼如下:
功能A-1的腳本:A1Test.java
public static void main(String[] args) throws Exception {
// A1測試邏輯實(shí)現
……
Class.forName("oracle.jdbc.driver.OracleDriver");
String url = "jdbc:oracle:thin:@localhost:1521:cui";
Connection conn = DriverManager.getConnection(url, "cui", "cui");
……
}
功能A-2的腳本:A2Test.java
public static void main(String[] args) throws Exception {
// A2測試邏輯實(shí)現
……
Connection conn = null;
try {
Class.forName("oracle.jdbc.driver.OracleDriver");
String connectionUrl = "jdbc:oracle:thin:@localhost:1521:cui";
conn = DriverManager.getConnection(connectionUrl, "cui", "cui");
……
}
這樣一來(lái)A1和A2的測試腳本都可以獨立運行,也不需要什么自動(dòng)化框架,但是原本相似的A1和A2功能,卻因為這樣的架構要寫(xiě)兩次代碼,如果TestA1和TestA2由兩個(gè)不同的程序員編寫(xiě),那么即便是如上面所示連接一個(gè)相同的數據庫,每個(gè)人需要自己寫(xiě)出實(shí)現方式,且都可能有不同的代碼風(fēng)格,這樣極大增加了測試代碼的編寫(xiě)和維護成本。
對于測試腳本而言,僅僅只需要負責測試邏輯本身,不應該負責諸如腳本啟動(dòng),管理的功能,同時(shí)降低提升測試腳本編寫(xiě)和維護成本,一些公用的方法例如數據庫連接等,都最好封裝成方法放在測試框架中,然后供測試腳本調用。
我在編寫(xiě)自動(dòng)化腳本時(shí),將每個(gè)測試功能點(diǎn)腳本作為一個(gè)Scenario類(lèi)供測試框架調用的,測試框架可以根據用戶(hù)輸入或者任務(wù)設定選擇執行哪些腳本。
TestFramework.excute(Scenario userInputScenarioName);
同時(shí),對于如數據庫連接查詢(xún)這樣的操作,也都做了封裝,在每個(gè)Scenario腳本里,編寫(xiě)者可以通過(guò)1行代碼就能查詢(xún)想要的數據:
在配置文件中填入數據庫信息:
#別名 db_dev #類(lèi)型 Oracle #IP 127.0.0.1 #端口 1521 #數據庫名 db1 #用戶(hù)名 cui #密碼 cui
在腳本文件中就可以這樣來(lái)訪(fǎng)問(wèn)數據庫:
String id = DB("db_dev").getSingleResult("select id from ……");
數據庫的鏈接,關(guān)閉工作都由框架進(jìn)行統一封裝。
二、測試數據與測試腳本脫離
測試腳本與測試框架脫離后,測試腳本更加專(zhuān)注于業(yè)務(wù)邏輯,但僅僅這樣是不是就夠了呢?對于同一個(gè)功能點(diǎn),我們往往也需要測很多數據,如果每個(gè)測試數據都“硬編碼”到測試腳本里,那么數據增加的時(shí)候又要將硬編碼的部分代碼復制粘貼,犯了和先前一樣的毛�。�
// 測試腳本脫離測試框架后的A1Test
class A1Test() {
……
// 測試A的實(shí)現過(guò)程
id = DB("db_dev").getSingleResult("select id from …… where coutry = 'CN'");
Assert(id == 100000);
id = DB("db_dev").getSingleResult("select id from …… where coutry = 'US'");
Assert(id == 200000);
id = DB("db_dev").getSingleResult("select id from …… where coutry = 'CA'");
Assert(id == 300000);
……
}
我們可以看到,雖然測試腳本TestA1的確不再負責程序啟動(dòng)這樣的雜事,同時(shí)數據鏈接也更加方便和規范,但是對于多個(gè)用例(country值不同,需要校驗的id大小不同),仍然需要復制粘貼代碼來(lái)實(shí)現。
因此我們也需要將測試數據從測試腳本中獨立出來(lái),實(shí)現“一個(gè)框架對應多個(gè)測試腳本,一個(gè)測試腳本對應多個(gè)測試數據”。
對于這樣的測試數據,往往是相對整齊規范的,我們可以用Excel,txt等文件,用表格的方式存儲測試數據,然后寫(xiě)出程序逐行讀取(jxl可以支持Excel2003以前Excel文件的讀取),每一行就是單次測試所需的數據。
用例ID 用例描述 是否執行 國家縮寫(xiě) 期望結果ID
用例ID | 用例描述 | 是否執行 | 國家縮寫(xiě) | 期望結果ID |
1 | 測試CN對應ID | y | CN | 100000 |
2 | 測試US對應ID | y | US | 200000 |
3 | 測試CA對應ID | y | CA | 300000 |
我采用jxl去讀取Excel數據,同時(shí)再對參數取用的方法進(jìn)行簡(jiǎn)化和封裝,最終可以用例如param("期望結果")這樣的方式返回當前執行的數據行對應列的數據。
// 測試數據與測試腳本脫離后的A1Test
class A1Test() {
……
// 測試A的實(shí)現過(guò)程
id = DB("db_dev").getSingleResult("select id from …… where coutry = " + param("國家縮寫(xiě)"));
Assert(id == param("期望結果ID"));
……
}
三、總結
測試腳本從測試框架脫離,即是將“一個(gè)測試腳本負責整個(gè)測試執行過(guò)程”的設計思路變?yōu)椤皽y試腳本只負責業(yè)務(wù)邏輯,一個(gè)測試框架驅動(dòng)多個(gè)測試腳本完成測試”。當業(yè)務(wù)邏輯變化時(shí),我們可以只修改測試腳本和數據而無(wú)須修改測試框架,當測試數據需要增加和修改時(shí),我們可以只修改測試數據而無(wú)須修改測試腳本,這樣的思路歸根結底還是來(lái)源于面向對象,但不管怎樣,提高效率,降低成本才是最終的目的。
文章來(lái)源于領(lǐng)測軟件測試網(wǎng) http://kjueaiud.com/