數據驅動 vs 關鍵字驅動:對UI自動化測試框架搭建的探索

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" "},{"type":"text","marks":[{"type":"strong"}],"text":"UI自動化測試用例剖析"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" 讓我們先從分析一端自動化測試案例的代碼開始我們的旅程。以下是我之前寫的一個自動化測試的小Demo。這個Demo基於Selenium與Java。由於現在Selenium在自動化測試的統治地位,並且隨着Selenium 4的即將發佈,在未來很長的一段時間裏這種統治地位應該還會持續,所以我的這篇文章還都是基於Selenium與Java的。"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/d2/d2ddce514485a14a42208cce297856a0.png","alt":null,"title":"自動化測試小demo","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"它要測試的東西其實是要看一下百度搜索能不能返回興業銀行的官網。我們分析一下這段代碼都包含些什麼東西。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"第一,這段代碼包含了定位符。熟悉Selenium的都知道,inpubBox和searchButton就是網頁的元素,通過By.id實例化,然後driver在findElement 的時候是通過他們的id,就是”kw”和”su”找到他們的。所以”kw” 和”su”就是他們的定位符。"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/1f/1ff536cc3622daa4a5d33f868428d3f7.png","alt":null,"title":"定位符","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"第二,這段代碼包含測試數據。“興業銀行”,“興業銀行歡迎您”就是測試要輸入的數據。"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/d3/d311b78735c0438549144a80ad51a6bb.png","alt":null,"title":"測試數據","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"第三部分沒前面兩部分那麼直觀。這段代碼在基於”kw”找到inputBox後,往裏填入“興業銀行”四個字,然後點擊了searchButton提交搜索申請,然後在搜索結果裏面尋找“興業銀行歡迎您”的text,然後以是否找到這個text作爲assert的標準。這些測試步驟反映的是真實的業務邏輯, 並且不能隨意更換順序。所以第三部分是業務邏輯。"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/1c/1cf5148ca110b69c0f3fb477f5ea1759.png","alt":null,"title":"業務邏輯","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"第四部分是業務邏輯裏面的每一個步驟的具體操作,例如輸入某段文字,或者點擊一個按鈕等。具體到我們的例子就是sendKeys和click。"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/9b/9b71614c394e0e2a8a47cb7a7563da52.png","alt":null,"title":"具體操作","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"第五部分就是如果把前面四部分都抽出去後,測試代碼剩下的東西,基本上就是一些負責準備或者是清理的代碼,例如初始化driver等。我把它稱爲代碼骨架。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"經上分析,一個自動化測試用例由五部分組成:定位符、測試數據、業務邏輯、具體操作、代碼骨架。"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/88/88d21b2010fc77f545dca7ff72f761fc.png","alt":null,"title":"UI自動化測試代碼結構","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"數據驅動的自動化測試"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果把測試數據抽取出去,通過數據的改變驅動自動化測試的執行,最終引起測試結果的改變,就是數據驅動的自動化測試。說白了,就是測試數據參數化。"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/58/585ecfa7ac2d892348915769f2961f6f.png","alt":null,"title":"數據驅動的UI自動化測試","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"數據驅動的自動化測試適合測試場景與業務邏輯相對簡單,變化不是特別大,但是測試數據對測試結果影響大的情景,或者是需要通過大量不同的測試數據對相同的測試場景展開測試的情景。它實現的方式比較簡單,但由於業務邏輯還是鑲嵌於測試代碼裏面的,業務邏輯與測試代碼還是強耦合的,一旦業務場景發生改變,需要修改測試代碼來適應業務的變化。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" 想要隔離業務對測試代碼的影響,就必須使用關鍵字驅動的方法。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" "},{"type":"text","marks":[{"type":"strong"}],"text":"關鍵字驅動的自動化測試"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"簡單來說,關鍵字驅動的自動化測試,就是在數據驅動的基礎上,把具體操作抽取到代碼以外,通過具體操作的改變來驅動測試的執行。這裏的說的關鍵字其實就是具體的操作,例如例子裏面的sendKeys和click。但由於具體操作(關鍵字)是基於業務邏輯的,要想把關鍵字抽取,業務邏輯也得一同抽取,才能實現真正的關鍵字驅動。同時,具體操作的對象是定位符定位的元素,所以定位符也必須得一同抽取出代碼意外,才能完全隔離業務對代碼的影響。"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/dc/dcd3a7caef255ee59551932e790642c7.png","alt":null,"title":"關鍵字驅動的UI自動化測試","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"基於關鍵字驅動的自動化測試適合業務場景複雜,測試步驟繁多的情景,又或者是希望搭建統一測試平臺的情景。它最大的優點是使得不懂編程的業務人員可以在不需要讀懂代碼的情況下,通過更改配置,自由添加新的測試用例或修改現有的測試用例。這一點在敏捷開發裏面相當實用,可以把業務捲入到測試當中,讓測試用例的準備工作儘可能提前;另外,測試代碼與要測試的業務邏輯完全隔離,同一份測試代碼可以複用於不同的測試場景,較少了重複開發的成本。但是,它也不是完全沒有缺點。首先,並不是所有場景都需要基於關鍵字驅動的,有些場景數據驅動的方法能解決的話就應該果斷選用數據驅動,而不應該過度設計;第二,由於業務邏輯與代碼隔離了,代碼的可讀性將大大降低,單純的代碼基本沒有業務含義,要想通過讀測試代碼來了解業務,基本是不可能了。犧牲了代碼的可讀性來換取可重用性。第三,由於代碼複用了,每一個測試用例的執行都相當於一次全新的測試,這意味着如果不加額外處理,測試報告將被互相覆蓋,永遠都只保留最後一個測試用例的執行結果。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" "},{"type":"text","marks":[{"type":"strong"}],"text":"一個關鍵字驅動的自動化測試例子"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":" "},{"type":"text","text":"這是我很早之前寫的一個關鍵字驅動的自動化測試的Demo。基本的想法是把定位符、測試數據、業務邏輯、具體操作多抽取到一個叫做testCase.properties的文件。Parameter用於存貯從testCase.properties讀出來的屬性,主測試類TestBankIndex通過讀取這個文件來驅動測試的執行。關鍵字所對應的具體操作放在了PageAction裏面。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在testCase.Properties裏面,通過testCaseNameList來定義測試用例名字,中間用|分割開。testCaseNameList以下,是每一個測試用例的具體測試步驟。每個測試步驟名字以測試用例名字作爲前綴,並且其排列順序就是測試步驟的順序。然後測試步驟的值以測試所需的操作(關鍵字)、定位方式、定位表達式、測試數據、超時時間排列得來,中間以|分割。這裏有一個比較特別的是斷言。如果是斷言的話,會有Keyword,用於斷言的判斷。"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/ab/abb5c25f07e53d541c80256372a5a956.png","alt":null,"title":"testCase.Properties","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Parameter用於存儲testCase.Properties的屬性。它其實只是一個簡單的JavaBean。"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/09/09fc09407a18a3f59dfa6e1194263c62.png","alt":null,"title":"Parameter","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"PageAction用於定義每個關鍵字對應的具體操作。我們以search方法爲例,通過傳入Parameter來獲取search需要的元素定位於要輸入的搜索文本,然後執行真正的搜索操作,並等待特定的時間。"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/9f/9f0745567472a0d66b4aa7c9a45ded88.png","alt":null,"title":"PageAction的Search方法","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"TestBankIndex是主測試類,但實際上裏面就一個testEachCase方法。裏面大量使用Java 反射機制來實例化和執行方法。所以,testEachCase在執行的時候,它不到最後一刻都不知道它要運行哪個測試用例與執行什麼操作的,這些完全在testCase.properties裏面定義。"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/10/10cc7aa21ebebb851c5d3333571bf237.png","alt":null,"title":"TestBankIndex的testEachCase方法","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"基本上就是這樣。代碼是一年前寫的,寫得比較挫,也沒時間優化,很多地方都寫的不是很規範,也沒有單元測試;測試報告覆蓋的問題也沒有處理;testCase.properties過於重量級了,什麼都往裏塞。還沒有UI,不方面操作。但作爲一個MVP,基本上實現了UI自動化測試框架所需要的基本功能。有興趣的同學可以通過以下GitHub鏈接clone代碼。希望藉此給大家在搭建自動化測試框架上提供一些思路,拋磚引玉。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"GitHub:https://github.com/fantasy130/autotest.git"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章