《我所知道的軟件測試自動化》- 關(guān)鍵字驅(qū)動的過去和未來
鑒于cnblogs的排版問題,PDF格式下載點擊這里。關(guān)鍵字驅(qū)動的過去和未來.pdf
版權(quán)聲明:本文可以被轉(zhuǎn)載,但是在未經(jīng)本人許可前,不得用于任何商業(yè)用途或其他以盈利為目的的用途。本人保留對本文的一切權(quán)利。如需轉(zhuǎn)載,請在轉(zhuǎn)載是保留此版權(quán)聲明,并保證本文的完整性。也請轉(zhuǎn)貼者理解創(chuàng)作的辛勞,尊重作者的勞動成果。
作者:陳雷 (Jackei)
Blog:http://jackei.cnblogs.com
鑒于這個系列寫的內(nèi)容是希望幫助“大多數(shù)2-3年工作經(jīng)驗、急切盼望提升自身能力的 tester找到捅破‘測試自動化’窗戶紙的辦法”,所以木有高深內(nèi)容,高手們請直接飄過,呵呵。
“關(guān)鍵字驅(qū)動”的由來
說到“關(guān)鍵字驅(qū)動”和“測試自動化”,就不能不提到 Mosley Daniel 的《軟件測試自動化》一書,這本書03年引入國內(nèi),04年市面上開始有賣,書中有兩個相信能吸引到很多 tester 的話題:
(1)腳本應(yīng)該錄制還是編寫?——想知道答案的自己下載電子書看吧:)
(2)“數(shù)據(jù)驅(qū)動”和“關(guān)鍵字驅(qū)動”。
彼時的我,剛剛經(jīng)歷了一次不成功的自動化實踐,雖然Rational Robot 提供了類似UI庫管理,數(shù)據(jù)池管理之類的強大功能,但是痛苦依然。對測試自動化理解的不夠深入,不知道該如何應(yīng)對RAD模式下UI和業(yè)務(wù)快速調(diào)整,以及對C/S下非標(biāo)準(zhǔn)控件的識別等問題,導(dǎo)致了無法快速維護(hù)腳本、replay 次數(shù)不夠,回放通過率不夠,最后的結(jié)果是ROI無法滿足要求,自動化項目宣告結(jié)束。
帶著很多疑問的我原本想從那本書中找到些答案,遺憾的是那時功力實在太差,居然沒有看懂,唯一留下印象的就是作者在80年代就開始探索自動化回歸的技術(shù),并且在90年代已經(jīng)嘗試了“數(shù)據(jù)驅(qū)動”和“關(guān)鍵字驅(qū)動”的技術(shù),想來當(dāng)時 Robot framework 之類的都還沒有出現(xiàn),所以我相信“關(guān)鍵字驅(qū)動”的技術(shù)源自這書的作者和他的朋友們。
2. 從“數(shù)據(jù)驅(qū)動”到“關(guān)鍵字驅(qū)動”
所謂的數(shù)據(jù)驅(qū)動,原本沒有什么特別的,無非就是把hard code 在腳本中的數(shù)據(jù)參數(shù)化出來,之所以算是Robot、WinRunner甚至QTP時代測試工具的賣點,其實主要是因為那個年代大多數(shù)system tester 不懂開發(fā),總需要有個功能來幫助自己完成參數(shù)抽取、數(shù)據(jù)維護(hù)、自動替換之類的功能。
而關(guān)鍵字驅(qū)動,則進(jìn)一步在技術(shù)上把 tester 分成了完全不懂技術(shù)的和懂點技術(shù)的,前者只能根據(jù)格式填寫一下 excel 表格,后者對工具/框架內(nèi)置的所謂關(guān)鍵字庫進(jìn)行增補或二次開發(fā)。
找些例子來看看吧。
(1)簡單的數(shù)據(jù)驅(qū)動。
如下面代碼,定義了一個class并實例化了幾個對象,用來存放不同的用戶登錄信息;而在負(fù)責(zé)完成登錄操作的 do_login_as() 方法中,把 send_keys 原來向表單中填充的數(shù)據(jù)參數(shù)化,根據(jù)每次調(diào)用 do_login_as() 方法時傳入的不同對象來實現(xiàn)用不同帳號登錄的效果——雖然我對以“登錄”為樣例介紹自動化測試腳本深惡痛絕,不過這里為了表述簡單,還是用了。(下面的代碼只是一個示例,不是直接用來運行的。)
其實數(shù)據(jù)驅(qū)動本來也沒有什么技術(shù)含量,寫過代碼的誰還不知道什么是變量啊?不過在早期的商業(yè)工具中,的確把這個作為了一個賣點,畢竟10年前的測試工具設(shè)計思路也與現(xiàn)在不同。早期的工具始終都假設(shè)”系統(tǒng)測試工程師不懂開發(fā)“,所以他們在開發(fā)測試腳本時需要借助類似錄制回放+編輯調(diào)試的模式;另外,對于數(shù)據(jù)驅(qū)動所需要的測試數(shù)據(jù),最好也是通過工具內(nèi)置的data pool 管理,通過表格編輯,甚至讀取 csv、excel 文件之類的。如果我們依照這個思路去實現(xiàn)自己的測試框架,那肯定是商業(yè)工具很有價值,畢竟自己實現(xiàn)data pool管理、外部數(shù)據(jù)文件讀取之類的功能,代碼量也不小,要處理好那些數(shù)據(jù)格式和內(nèi)容校驗之類的,貌似跟我們所需要完成的自動化也沒啥太大關(guān)系。
可問題在于,為什么一定要有data pool+外部數(shù)據(jù)文件來實現(xiàn)”數(shù)據(jù)驅(qū)動“呢?
(2)傳統(tǒng)的“關(guān)鍵字驅(qū)動”
還是先用個例子來看看傳統(tǒng)的“關(guān)鍵字驅(qū)動”長什么樣子。
New Test Case | ||
open |
| |
type | q | 關(guān)鍵字驅(qū)動 |
clickAndWait | btnG |
|
verifyTextPresent | 關(guān)鍵字驅(qū)動 |
|
上面是一個 Selenium 0.x 時代Core模式下(什么是selenium的core模式和RC模式請自行google,不過話說現(xiàn)在已經(jīng)全民webdriver時代了,不知道也就不知道吧)的例子(QTP的實現(xiàn)效果也類似),簡單說就是第一行是 test case name,下面3列開始進(jìn)入正題,第一列表示你想干啥,第二列是被操作對象是誰,第三列是你對它干了啥。06年剛剛開始嘗試web測試自動化的時候,我們一度認(rèn)為這是一個比之前的測試工具要智能的多的東東,不過在嘗試了很長一段時間之后,還是發(fā)現(xiàn)了這種模式下的問題,又逐漸轉(zhuǎn)回了編寫腳本的方式。
對比上面的第一個腳本,可以看出關(guān)鍵字驅(qū)動進(jìn)一步屏蔽了底層的實現(xiàn)細(xì)節(jié),例如,你只需要clickAndWait那個btnG,而不需要知道btnG到底是個啥,以及它在哪里,你只需要填寫表格。等你把一個個 test case 變成了一個個表格,把一個個step變成了一行行表格中的內(nèi)容,基本上你的自動化測試就搞完了。——真的是這樣嗎?
現(xiàn)在回想一下,關(guān)鍵字驅(qū)動之所以會出現(xiàn),可能初衷還是為了降低自動化實施的門檻——因為tester都不太懂開發(fā)嘛,所以開發(fā)能力強的人把框架實現(xiàn)出來,原來那些只會寫excel的 system tester填寫表格就可以了。也許現(xiàn)在還有很多公司會傾向于這個方案,美其名曰“分工協(xié)作,人盡其能”。不過關(guān)于這個問題,暫時先不展開討論,先看看這種傳統(tǒng)的關(guān)鍵字驅(qū)動模式會遇到什么問題。
首先,頁面對象的識別問題。這是任何一種基于UI實現(xiàn)回歸測試自動化的框架都會遇到的問題。在C/S時代,就已經(jīng)出現(xiàn)了很多定制化UI組件難以被工具識別的問題,可好歹總逃不出windows的手心。到了web時代,前端實現(xiàn)技術(shù)百花齊放,而前端代碼的編寫也更是一個開發(fā)人員一個習(xí)慣,如果缺少統(tǒng)一的編碼規(guī)范,對于那些沒有使用id或者name之類屬性的頁面對象,傳統(tǒng)的關(guān)鍵字驅(qū)動框架就沒有例子中看起來那么美好了。當(dāng)然,有人說xpath可以解決一切問題。嗯,xpath是很強,但是一個沒有開發(fā)經(jīng)驗的tester想掌握它其實也不會比學(xué)會一點基本的編碼技術(shù)容易多少;另外,xpath并不是萬能的,在實際中還是有些它處理不了的情況。下面再給個例子(如果tester看不懂這個例子,估計學(xué)會xpath也有點困難):
上面這個圖中是一個表格,id列(第一列)和需求名稱列(第二列)下顯示的內(nèi)容,都是可以點擊的超鏈接,最后的“操作”一列中的一個個小圖標(biāo)也各自指向一個鏈接(分別是“變更”、“評審”、“編輯”、“建用例”),通過查看第一行記錄的html代碼,我們發(fā)現(xiàn):
假如想要編輯一條記錄,沒有可利用的id或name屬性,唯一可用的是link;
link指向的url中包含了這條記錄的ID信息,因為這里是第一行記錄的代碼,所以都是341,而后面的每一行記錄的代碼都是各自的ID。
上面這個例子是企業(yè)應(yīng)用中常見的場景,關(guān)鍵問題在于數(shù)據(jù)記錄ID是一個不可控因素,而數(shù)據(jù)記錄的title/name之類的是可控的,為了提高test case的可維護(hù)性和相互獨立,我們肯定不會依賴ID去模擬頁面操作,而是根據(jù)title/name之類取回ID信息,再拼裝回所需要操作的鏈接。這種情況下,xpath恐怕也搞不定了。(如果這里再涉及到要在幾個iframe之間跳來跳去,恐怕寫腳本的人就要崩潰了。)
當(dāng)然,有人會說關(guān)鍵字驅(qū)動框架一般也可以定義function來實現(xiàn)類似的操作啊。唉,都到了編寫自定義function的地步了,干嘛還非要糾纏在關(guān)鍵字驅(qū)動上啊。
第二,腳本的可維護(hù)性問題。如一開始的例子,傳統(tǒng)的關(guān)鍵字驅(qū)動是一種純“面向過程”的腳本組織方式(就像C/pascal),表格中填寫的是一個操作序列。如果多個test case 都涉及到某個頁面,基本上就會在多個case中都看到類似 clickAndWait btnG這樣的內(nèi)容,而一旦頁面中btnG改名叫buttonG了,或者 clickAndWait btnG與verifyTextPresent 之間增加了一個 clickAndWait XXX的step,那基本上每個case都需要修改。
嗯,問題來了,當(dāng)你的腳本數(shù)量從1增長到100、1000的時候,當(dāng)UI的變動無法避免的時候,當(dāng)你發(fā)現(xiàn)100個case回歸執(zhí)行只有90個通過,執(zhí)行失敗的10個需要逐個檢查錯誤日志和查看截圖,再挨個修改的時候,當(dāng)下次回歸測試又發(fā)生這種問題的時候——基本上這就是一個死循環(huán)了。如果解決不了根本問題,前期投資可以舍棄了,別糾結(jié)了。
當(dāng)然,有人說關(guān)鍵字驅(qū)動已經(jīng)進(jìn)化了,可以跟最新的webdriver結(jié)合起來,提升關(guān)鍵字的封裝層次,解決這個可維護(hù)性的問題。好吧,這個問題等下再詳細(xì)說。
第三,腳本的可擴(kuò)展性問題。在大規(guī)模實施自動化的過程中,腳本一般都會簡單的是一行行的browser.find_elmenet_by_id(xxxx).click() 這樣的模式,根據(jù)各種條件來判斷執(zhí)行的分支,進(jìn)行各種異常處理,第三方類庫/包的調(diào)用,主機環(huán)境的訪問,諸如此類,這些對于所有的3GL/4GL來說其實都很容易實現(xiàn),但對于傳統(tǒng)的關(guān)鍵字驅(qū)動來說,嗯,也可以實現(xiàn),大概是下面這個樣子【摘自robot framework(此robot非rational robot)】:
下面是一個在 keyword 表格里面實現(xiàn)的 FOR 循環(huán):
:FOR | ${var} | IN | @{SOME LIST} |
| Run Keyword If | '${var}' == 'EXIT' | Exit For Loop |
| Do Something | ${var} |
|
這是獲取時間,當(dāng)然也是寫在表格里面的:
${time} = | Get Time |
|
|
|
${secs} = | Get Time | epoch |
|
|
${year} = | Get Time | return year |
|
|
${yyyy} | ${mm} | ${dd} = | Get Time | year,month,day |
@{time} = | Get Time | year month day hour min sec |
|
|
${y} | ${s} = | Get Time | seconds and year |
|
import 外部的庫:
Import Library | MyLibrary |
|
|
|
Import Library | ${CURDIR}/Library.py | some | args |
|
Import Library | ${CURDIR}/../libs/Lib.java | arg | WITH NAME | JavaLib |
讀取外部文件:
Import Resource | ${CURDIR}/resource.txt |
Import Resource | ${CURDIR}/../resources/resource.html |
正則表達(dá)式:
${escaped} = | Regexp Escape | ${original} |
@{strings} = | Regexp Escape | @{strings} |
重復(fù)執(zhí)行5次 goto pre page 操作:
Repeat Keyword | 5 times | Goto Previous Page |
|
|
Repeat Keyword | ${var} | Some Keyword | arg1 | arg2 |
執(zhí)行一個 keyword 然后期待能捕獲一個異常:
Run Keyword And Expect Error | My error | Some Keyword | arg1 | arg2 |
${msg} = | Run Keyword And Expect Error | * | My KW |
|
Should Start With | ${msg} | Once upon a time in |
|
|
有人可能會說“你看,關(guān)鍵字驅(qū)動框架也可以擴(kuò)展的很強大啊!”。是,在programming 的世界中,沒有什么不能做的,不過都弄到這個份兒上了,學(xué)習(xí)這一套東西跟學(xué)習(xí)一個標(biāo)準(zhǔn)的編程語言還有什么差別嗎?先不說這樣的框架越擴(kuò)展越難維護(hù),可靠性也就越差,單單這些關(guān)鍵字的用途被局限在自己的框架中,你所積累的知識和經(jīng)驗無法重用到其他測試代碼的編寫中這一個理由,就應(yīng)該徹底放棄這種方式了。
如果要說的直白一些,傳統(tǒng)的關(guān)鍵字驅(qū)動框架的時代在前幾年就已經(jīng)開始遠(yuǎn)去(是had been,不是have been),我們感謝上一代tester的努力探索和實踐,但最終歷史證明這是一個不算成功的嘗試,一個框架如果不具備開放性,一切都自給自足,那么有一天這也會成為限制自己發(fā)展的最大原因。
(3)穿馬甲的“關(guān)鍵字驅(qū)動”
時代在進(jìn)步,關(guān)鍵字驅(qū)動也在進(jìn)步,這個領(lǐng)域中的代表 robot framework(此robot非rational robot) 也在進(jìn)步,于是,test case 變成了下面這個樣子。
Test Case | Action | Argument | Argument |
---|---|---|---|
User can create an account and log in | Create Valid User | fred | P4ssw0rd |
| Attempt to Login with Credentials | fred | P4ssw0rd |
| Status Should Be | Logged In |
|
|
|
|
|
User cannot log in with bad password | Create Valid User | betty | P4ssw0rd |
| Attempt to Login with Credentials | betty | wrong |
| Status Should Be | Access Denied |
|
依舊不變的是“表格”,改變的是填寫方式——其實這背后的,是關(guān)鍵字定義終于被開放出來,tester可以自己定義keyword然后“注冊”到框架中,而那些依然沒有學(xué)會基本編程技能的tester,繼續(xù)用這些keyword重復(fù)上個時代的事情——填寫表格。
其實相對于最初對關(guān)鍵字驅(qū)動的定義,這個真的已經(jīng)不是關(guān)鍵字驅(qū)動了,如果非說它是,那么只能說上個時代的關(guān)鍵字驅(qū)動中,test case 表格的每行都是一個頁面操作,而“新的”關(guān)鍵字驅(qū)動中,test case 的每行都已經(jīng)是一個完整的業(yè)務(wù)操作,以上面的“Create Valid User” step 為例,robot framework希望的實現(xiàn)方式是tester通過python等4GL實現(xiàn)一個同名的function,這個function接受兩個參數(shù),分別是“fred”和“P4ssw0rd”,再把這個function注冊到robot framework中。而“Create Valid User”內(nèi)部的實現(xiàn),可以類似于一開始“數(shù)據(jù)驅(qū)動”中的那個例子,充分利用4GL的特性和已有的其他第三方組件(例如webdriver),來實現(xiàn)各種復(fù)雜的基于UI的操作,這樣也就解決了剛剛“傳統(tǒng)的關(guān)鍵字驅(qū)動”所遇到的問題。
最后,當(dāng)完成了這個function的開發(fā)并在robot framework中注冊后,做手工測試的system tester就可以很容易的把原本excel中的一個個case轉(zhuǎn)變?yōu)樽詣踊_本了。
其實這個思路有它的優(yōu)點,例如:通過分工協(xié)作降低實施門檻,可以一開始就編寫符合robot所需格式的manual test case,等到keyword開完全了以后這些case就可以直接導(dǎo)入執(zhí)行了;不再自給自足,而是保持一定的開放,并利用其他第三方組件的特性。這樣很大程度上解決了自動化項目實施遇到的人員能力問題和可維護(hù)性、可擴(kuò)展性的問題。
另外,新的關(guān)鍵字驅(qū)動還有一個更加先進(jìn)的“近親”BDD作為參照,很容易把它的一些實踐也一起融合進(jìn)來。
一切看起來都很美好,不過問題也還是有。
表格化的test case畢竟不同于編寫代碼,調(diào)試就變成了一個問題,如果寫錯了關(guān)鍵字的一個字母,要及時發(fā)現(xiàn)并定位到問題就不那么容易。當(dāng)然,可以再開發(fā)一個web平臺,讓編寫case的人僅能從一個list中選擇已經(jīng)定義好的keyword,不過這個成本恐怕就不是一般研發(fā)團(tuán)隊能承受的了。
作為一個軟件,易用性和復(fù)雜度總是成反比的,當(dāng)框架提供了方便的表格化編寫case功能時,也相對的增加了底層的復(fù)雜度(雖然沒看過robot framework的代碼,但是相信底層代碼的分層也應(yīng)該比較復(fù)雜),對于想要真的掌握框架的團(tuán)隊來說,無形中增加了一道門檻。另外,復(fù)雜度與可擴(kuò)展性也是成反比的,就像我可以用木頭做一輛車,也可以把木頭車拆了做些別的東西,但是我沒法把一輛汽車拆了弄成別的東西——前兩年廣東美院那位把解放卡車拆了做成關(guān)公像的牛人除外。當(dāng)然,最終實施自動化時到底如何進(jìn)行框架選型,就要團(tuán)隊自己在易用性/復(fù)雜度/可擴(kuò)展性上進(jìn)行評估了。
把excel里面的manual test case通過新的關(guān)鍵字驅(qū)動直接變成可執(zhí)行的腳本是最好的方法嗎?這似乎只是一個傳統(tǒng)system test 的慣性思維在作怪,為什么沒看過開發(fā)人員把unit test 也寫到一個個表格里面?為什么manual test case 就一定要先寫在excel里面,而不是一開始就是代碼?
如果僅僅是考慮把 step組裝起來,再把case組織成suite執(zhí)行,其實代碼實現(xiàn)上可以說毫無技術(shù)含量,但是對于一個沒有開發(fā)經(jīng)驗的tester來說,這畢竟是一個跟coding簡單親密接觸的機會,可以讓tester從低難度的代碼開始培養(yǎng)興趣和信息。而keyword,無論新的還是舊的,卻剝奪了這個機會;當(dāng)tester希望學(xué)習(xí)框架的時候,會發(fā)現(xiàn)表格的層面跟下層框架之間的不是樓梯,而是一道溝。
3. “關(guān)鍵字驅(qū)動”的未來
我們?nèi)缃袼幍沫h(huán)境總是在變化著,今天與10年前相比,最大的變化就是測試行業(yè)獲得了極大的發(fā)展,大多數(shù)企業(yè)都認(rèn)可了測試工作的重要性,并且開始思考如何提升測試工作自身的質(zhì)量和效率,而且不同規(guī)模的企業(yè)都在探索著合適自己的研發(fā)流程和技術(shù);而tester們的技術(shù)能力也在不斷增強,至少能寫代碼的人比5年前多了很多。當(dāng)然,還要感謝開源世界帶來的眾多框架、組件,讓自動化的門檻不斷降低。
就像傳統(tǒng)的關(guān)鍵字驅(qū)動已經(jīng)遠(yuǎn)去一樣,新的關(guān)鍵字驅(qū)動未來會如何,大家討論吧。:-)
接下來,準(zhǔn)備討論下“Page object model”技術(shù),或者如何構(gòu)建一個輕巧但功能完備的web測試框架。