摘要:在本文中,和大家探論下如何編寫(xiě)大量自動(dòng)化接口用例以及保持結(jié)果穩(wěn)定。如通過(guò)特定名字的變量名和數(shù)據(jù)進(jìn)行關(guān)聯(lián)。如果執(zhí)行過(guò)程需要依賴其他系統(tǒng)的接口的話,那么其他系統(tǒng)發(fā)生了變更或故障就會(huì)影響自身用例的進(jìn)行。使用不同賬號(hào)等進(jìn)行隔離。
引言
當(dāng)一個(gè)新人剛加入公司的時(shí)候,我們通常告訴新人怎么去寫(xiě)一個(gè)自動(dòng)化用例:從工程配置到如何添加接口、如何使用斷言,最后到如何將一個(gè)用例運(yùn)行起來(lái)。
而在實(shí)際工作和業(yè)務(wù)場(chǎng)景中,我們常常面臨著需要編寫(xiě)和組織一堆用例的情況:我們需要編寫(xiě)一個(gè)業(yè)務(wù)下的一系列的自動(dòng)化接口用例,再把用例放到持續(xù)集成中不斷運(yùn)行。面臨的問(wèn)題比單純讓一個(gè)用例運(yùn)行起來(lái)復(fù)雜的多。
本人加入有贊不到一年,從寫(xiě)下第 1 個(gè) case 開(kāi)始,持續(xù)編寫(xiě)和運(yùn)行了 1000 多個(gè) case ,在這過(guò)程中有了一些思考。在本文中,和大家探論下如何編寫(xiě)大量自動(dòng)化接口用例以及保持結(jié)果穩(wěn)定。
一、執(zhí)行效率目前使用的測(cè)試框架是基于 spring ,被測(cè)接口是 dubbo 的服務(wù)。 dubbo 的架構(gòu)如圖(源自官網(wǎng))
服務(wù)使用方的初始化需要經(jīng)歷以下這幾個(gè)步驟:
監(jiān)聽(tīng)注冊(cè)中心
連接服務(wù)提供端
創(chuàng)建消費(fèi)端服務(wù)代理
本地調(diào)試用例時(shí),發(fā)現(xiàn)速度非常慢,運(yùn)行一個(gè)用例需要 30s,而實(shí)際執(zhí)行用例邏輯的時(shí)間大概在 1s 左右,主要時(shí)耗在服務(wù)消費(fèi)者的初始化階段。
測(cè)試工程中,各服務(wù)的 test 類繼承了同一個(gè)基類,基類里面做了各服務(wù)的初始化的步驟。在對(duì)接的服務(wù)數(shù)目較少時(shí),需要初始化的對(duì)象較少,對(duì)用例運(yùn)行的影響并不大,但隨著業(yè)務(wù)的增多,服務(wù)數(shù)目也增多,導(dǎo)致跑 A 服務(wù)接口的用例時(shí)把大量未用到的 B 服務(wù)、C 服務(wù)也一起初始化了,導(dǎo)致整體時(shí)耗大大增加。
解決辦法:在運(yùn)行用例時(shí)只初始化需要的服務(wù)使用方,減少不必要的初始化開(kāi)銷。
二、用例編寫(xiě)和維護(hù) 一個(gè)用例示例以一個(gè)簡(jiǎn)單的業(yè)務(wù)場(chǎng)景為例:商家可以在后臺(tái)創(chuàng)建會(huì)員卡給店鋪的會(huì)員領(lǐng)取,商家可以對(duì)會(huì)員卡進(jìn)行更新操作,這里需要有一個(gè)自動(dòng)化用例去覆蓋這個(gè)場(chǎng)景。
用例編寫(xiě)的基本步驟為:
step 1 :準(zhǔn)備數(shù)據(jù)構(gòu)造新建會(huì)員卡和更新會(huì)員卡的對(duì)象
step 2 :執(zhí)行創(chuàng)建會(huì)員卡
step 3 :執(zhí)行更新會(huì)員卡
step 4 :檢查更新結(jié)果
step 5 :清理創(chuàng)建的會(huì)員卡
轉(zhuǎn)換成代碼為:
@Test public void testUpdate() { try { /* * 創(chuàng)建新建和更新的卡對(duì)象 */ CardCreateDescriptionDTO descCreate = new CardCreateDescriptionDTO(); descCreate.setName(xxxx); //此處省略若干參數(shù)設(shè)置過(guò)程.... CardUpdateDescriptionDTO descUpdate = new CardUpdateDescriptionDTO(); descUpdate.setName(xxxxx); //此處省略若干參數(shù)設(shè)置過(guò)程.... /* * 新建會(huì)員卡 */ cardAlias = cardService.create((int) kdtId, descCreate,operator).getCardAlias(); /* * 更新會(huì)員卡 */ cardService.update(kdtId, cardAlias, descUpdate, operator); /* * 校驗(yàn)編輯是否生效 */ CardDTO cardDTO = cardService.getByCardAlias(cardAlias); Assert.assertEquals(cardDTO.getName(), xxxx, "會(huì)員卡更新失敗"); //此處省略若干參數(shù)校驗(yàn)過(guò)程.... } catch (Exception e) { Assert.assertNull(e); } finally { try { if(cardAlias!=null) { cardService.deleteByCardAlias((int) kdtId, cardAlias, operator); } } catch (Exception e) { Assert.assertNull(e, e.getMessage()); } } }
按照預(yù)期的步驟去寫(xiě)這個(gè) case ,可以滿足要求,但是如果需要擴(kuò)展一下,編寫(xiě)諸如:更新某種類型的會(huì)員卡、只更新會(huì)員卡的有效期這樣用例的時(shí)候,就會(huì)覺(jué)得按這個(gè)模式寫(xiě) case 實(shí)在太長(zhǎng)太啰嗦了,痛點(diǎn)在以下幾個(gè)地方:
數(shù)據(jù)準(zhǔn)備比較麻煩,需要逐一設(shè)值
數(shù)據(jù)檢查部分逐字段檢查,心好累
每個(gè)創(chuàng)建相關(guān)的用例都需要清理資源,每次都需要做一次,太重復(fù)了
用例本身關(guān)注的是更新這個(gè)操作,卻花了太多時(shí)間和精力在其他地方,很多是重復(fù)勞動(dòng)。代碼編寫(xiě)里有一個(gè)重要原則,DRY(Don"t Repeat Yourself),即所有重復(fù)的地方都可以考慮抽象提煉出來(lái)。
三段式用例可以將大部分用例的執(zhí)行過(guò)程簡(jiǎn)化為三個(gè)部分:
數(shù)據(jù)準(zhǔn)備
執(zhí)行操作
結(jié)果檢查
用簡(jiǎn)單的三個(gè)部分來(lái)完成上述用例的改寫(xiě):
數(shù)據(jù)準(zhǔn)備:
@DataProvider(name="dataTestUpdate") public Object[][] dataTestUpdate() { return new Object[][]{ {cardFactory.genRuleNoCreate(...),cardFactory.genRuleNoUpdate(...)}, {cardFactory.genRuleCreate(...),cardFactory.genRuleUpdate(...)}, {cardFactory.genPayCreate(...),cardFactory.genPayUpdate(...)} }; }
執(zhí)行操作+結(jié)果檢查
Test(dataProvider = "dataTestUpdate") public void testUpdate(CardCreateDescriptionDTO desc,CardUpdateDescriptionDTO updateDesc){ try { /* * 執(zhí)行操作:創(chuàng)建+更新 */ //創(chuàng)建會(huì)員卡 CardDTO cardBaseDTO = createCard(kdtId,desc,operatorDTO); cardAlias=cardBaseDTO.getCardAlias(); recycleCardAlias.add(cardAlias); //將卡的標(biāo)識(shí)放入垃圾桶后續(xù)進(jìn)行回收 CardDTO ori = getCard(kdtId,cardAlias); //更新會(huì)員卡 updateCard(kdtId,cardAlias,updateDesc,operatorDTO); CardDTO updated = getCard(kdtId,cardAlias); /* * 結(jié)果檢查 */ checkUpdateCardResult(ori,updated,updateDesc,kdtId); } catch (Exception e) { Assert.assertNull(e); }
其中可行的優(yōu)化點(diǎn)將在下面娓娓道來(lái)。
測(cè)試數(shù)據(jù)的優(yōu)化在這個(gè)用例中,數(shù)據(jù)準(zhǔn)備的部分使用了 dataProvider 來(lái)復(fù)用執(zhí)行過(guò)程,這樣不同參數(shù)但同一過(guò)程的數(shù)據(jù)可以放在一個(gè) case 里進(jìn)行執(zhí)行和維護(hù)。
數(shù)據(jù)生成使用了工廠方法 CardFactory ,好處是簡(jiǎn)化了參數(shù),避免了大量 set 操作(本身包裝的就是 set 方法);另一方面,根據(jù)實(shí)際的業(yè)務(wù)場(chǎng)景,可以考慮提供多個(gè)粒度的構(gòu)造方法,比如以下兩個(gè)構(gòu)造方法需要提供的參數(shù)差別很大:
第一個(gè)主要用在驗(yàn)證創(chuàng)建接口的場(chǎng)景,檢查各個(gè)傳入的參數(shù)是否生效。
public CardCreateDescriptionDTO genRuleCreate(Boolean isPost,Integer discount,Long rate,Long pointsDef, String couponIds, Long num, Long growth,Long termToCardId,Long amount,Long points,Long trade){
第二個(gè)用在如刪除的場(chǎng)景,所以只需要一個(gè)創(chuàng)建好的會(huì)員卡對(duì)象,并不是很關(guān)注創(chuàng)建的內(nèi)容是什么。
public CardCreateDescriptionDTO genRuleSimpleCreate(String name){
在上面的優(yōu)化過(guò)的用例中,能夠執(zhí)行更新操作的前置條件是需要有一個(gè)已經(jīng)創(chuàng)建的會(huì)員卡,在實(shí)際用例編寫(xiě)的時(shí)候通過(guò)直接創(chuàng)建一個(gè)會(huì)員卡,然后執(zhí)行更新完成后再回收刪除這張會(huì)員卡來(lái)滿足這個(gè)條件。另一種提供滿足操作所需前置數(shù)據(jù)的方式是預(yù)置數(shù)據(jù)(預(yù)先生成數(shù)據(jù))。
以下情況可以考慮預(yù)置數(shù)據(jù)的方式:
提高用例穩(wěn)定性,解依賴,加快執(zhí)行速度
需要對(duì)特定的類型、狀態(tài)的對(duì)象進(jìn)行查詢
創(chuàng)建或者構(gòu)造比較麻煩
典型的場(chǎng)景:比如編寫(xiě)查詢的用例時(shí)預(yù)先創(chuàng)建滿足條件的對(duì)象供查詢用例使用。
談到預(yù)置數(shù)據(jù),不得不談的一個(gè)問(wèn)題是數(shù)據(jù)管理。在編寫(xiě)用例的時(shí)候,"我們往往需要一個(gè)____的資源",框框里面的即是對(duì)數(shù)據(jù)的描述和要求,比如我需要一個(gè)全新的賬號(hào),一個(gè)支付過(guò)的訂單號(hào),一張免費(fèi)的會(huì)員卡,來(lái)完成我們的用例。所以需要對(duì)數(shù)據(jù)進(jìn)行標(biāo)記而不是簡(jiǎn)單硬編碼的方式在用例中使用。
如:通過(guò)特定名字的變量名和數(shù)據(jù)進(jìn)行關(guān)聯(lián)。
/**只做查詢卡,不做領(lǐng)卡刪卡*/ public Long queryCardUid = DataMocker.MOCK_YZUID.get(1); /**用戶卡類操作,領(lǐng)卡刪卡*/ public Long takeCardUid = DataMocker.MOCK_YZUID.get(6); /**退款用*/ public Long refundCardUid =DataMocker.MOCK_YZUID.get(4);
對(duì)數(shù)據(jù)進(jìn)行標(biāo)記后,會(huì)發(fā)現(xiàn)有一部分?jǐn)?shù)據(jù)是用來(lái)驗(yàn)證寫(xiě)操作(如創(chuàng)建、更新),有一部分?jǐn)?shù)據(jù)是查詢使用。如果數(shù)據(jù)又要被寫(xiě)操作的 case 使用,又要被讀操作的 case 使用,那么寫(xiě)操作的問(wèn)題和異常就會(huì)影響讀操作 case 的執(zhí)行結(jié)果。所以,在代碼工程中,可以進(jìn)行約定,將讀寫(xiě)用到的資源進(jìn)行分離來(lái)降低數(shù)據(jù)的耦合:
查詢 case 用的賬號(hào)不做更改對(duì)象的操作
查詢 case 用的對(duì)象不做修改、刪除的操作
驗(yàn)證增、刪、改行為的資源使用特定賬號(hào),且資源最后做回收刪除處理(因?yàn)橘Y源總數(shù)有限)
最后,用例執(zhí)行完成后需要清理資源。這里的清理資源采用的是一個(gè)全局的 list 的方式保存需要清理的資源信息,在用例執(zhí)行過(guò)程中往里增加數(shù)據(jù):(recycleCardAlias.add(cardBaseDTO.getCardAlias());),
然后用對(duì)應(yīng)的方法取其中的數(shù)據(jù)進(jìn)行刪除,類似垃圾桶。與原有執(zhí)行完就執(zhí)行清理動(dòng)作相比,使用垃圾桶更加靈活,可以選擇控制下清理頻率。
比如每次在 AfterMethod 或 AfterClass 中去清理。
//統(tǒng)一回收 @AfterMethod public void tearDownMethod() { for(int i =0;i對(duì)方法的適度封裝 在實(shí)際編寫(xiě)用例的時(shí)候,有兩個(gè)地方可以考慮進(jìn)行方法封裝,從來(lái)簡(jiǎn)化調(diào)用,方便維護(hù):
封裝基本操作。如果刪除操作依賴創(chuàng)建操作,查詢操作依賴創(chuàng)建操作,那么創(chuàng)建操作可以看作是個(gè)基本操作,可以對(duì)創(chuàng)建操作包裝一下,將注意力關(guān)注于實(shí)際需要執(zhí)行和驗(yàn)證的地方??梢苑庋b的東西很多,有參數(shù)封裝、異常處理的封裝、一些輪訓(xùn)、重新邏輯的封裝。createCard()、getCard()、deleteCard方法就是將接口、參數(shù)組裝、檢查等封裝好的方法。
封裝檢查方法。上述用例中的檢查采用了一個(gè)檢查方法代替了以往的多個(gè)assert:
穩(wěn)定性
checkUpdateCardResult(ori,updated,updateDesc,kdtId); ,在方法里包裝了一些關(guān)鍵字段的比較,包括兩個(gè)對(duì)象之間成員是否一致的比較。所有的更新操作的結(jié)果都需要滿足:有變更的字段值變成新的值,未發(fā)生變更的值和原有一致。該方法實(shí)現(xiàn)了這種檢查邏輯,所以寫(xiě)更新操作用例的同學(xué)不需要關(guān)注如何校驗(yàn),而是關(guān)心如何更新,因?yàn)闄z查邏輯是現(xiàn)成的、通用的。將來(lái)檢查邏輯發(fā)生變更,也只需要維護(hù)這一個(gè)方法即可。當(dāng)大批量用例進(jìn)行運(yùn)行時(shí),用例集的失敗率會(huì)變得較高,幾個(gè)微小的瑕疵都會(huì)造成用例的失敗,此時(shí)我們需要更加關(guān)注用例的穩(wěn)定性。一些實(shí)踐中比較好的措施和方式:
減少外部依賴。如果執(zhí)行過(guò)程需要依賴其他系統(tǒng)的接口的話,那么其他系統(tǒng)發(fā)生了變更或故障就會(huì)影響自身用例的進(jìn)行??梢钥紤]通過(guò)預(yù)先生成的數(shù)據(jù)來(lái)替代調(diào)用外部接口生成數(shù)據(jù)在用例中使用。
預(yù)置數(shù)據(jù)代替創(chuàng)建過(guò)程。由于操作越多穩(wěn)定性越低,使用預(yù)置數(shù)據(jù)而不是實(shí)時(shí)生成它,速度更快,穩(wěn)定性更高。
使用不同賬號(hào)等進(jìn)行隔離。通過(guò)隔離,用例執(zhí)行失敗的臟數(shù)據(jù)就不會(huì)影響其他用例。
調(diào)優(yōu):超時(shí)、等待時(shí)間。線上超時(shí)時(shí)間設(shè)置的比較短,測(cè)試環(huán)境的機(jī)器配置不如線上,需要適時(shí)調(diào)大超時(shí)和等待時(shí)間來(lái)保證接口調(diào)用不會(huì)超時(shí)。
防御式編程。編寫(xiě)測(cè)試代碼時(shí)不能假設(shè)數(shù)據(jù)已存在或者沒(méi)有臟數(shù)據(jù)殘留,所以預(yù)先的判斷和清理很重要,比如檢查到數(shù)據(jù)缺失就實(shí)時(shí)修復(fù)、用例運(yùn)行之前考慮清除臨時(shí)數(shù)據(jù)。
定位并解決不穩(wěn)定的問(wèn)題。有時(shí)候偶現(xiàn)用例失敗,可以考慮給被測(cè)應(yīng)用增加日志,同時(shí)持續(xù)多次運(yùn)行用例多次(如 testNg 里增加threadPoolSize=1, invocationCount=50)來(lái)復(fù)現(xiàn)問(wèn)題,最終解決問(wèn)題。
總結(jié)對(duì)于大規(guī)模用例的編寫(xiě)、組織和運(yùn)行的問(wèn)題,文中從三個(gè)方面給出了有贊測(cè)試的實(shí)踐和思考:精簡(jiǎn)初始化來(lái)提高執(zhí)行速度、優(yōu)化用例編寫(xiě)降低編寫(xiě)和維護(hù)成本、多種方式提高用例穩(wěn)定性,希望能給大家一些啟發(fā)。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/94713.html
摘要:三思而后行自動(dòng)化測(cè)試最終目的是啥投入產(chǎn)出比的最佳平衡點(diǎn)在哪很多實(shí)施者在搭建自動(dòng)化框架前往往缺乏思考,為了自動(dòng)化而自動(dòng)化。 三思而后行 UI自動(dòng)化測(cè)試最終目的是啥?投入產(chǎn)出比的最佳平衡點(diǎn)在哪?很多實(shí)施者在搭建UI自動(dòng)化框架前往往缺乏思考,為了自動(dòng)化而自動(dòng)化。三思而后行,方向決定成敗。由于項(xiàng)目接口(API and Service)自動(dòng)化代碼行覆蓋率已經(jīng)達(dá)到70%,基于當(dāng)前自動(dòng)化人力和項(xiàng)目質(zhì)...
摘要:反之,好用例則是表現(xiàn)穩(wěn)定的用例??梢越y(cè)試或開(kāi)發(fā)人員壞用例檔案,并自動(dòng)追蹤每一個(gè)壞用例的來(lái)源,督促負(fù)責(zé)人跟進(jìn)解決。接下來(lái),需要做的就是大家共同維護(hù)好這樣一個(gè)最佳狀態(tài),避免破窗理論的發(fā)生。 摘要: 自動(dòng)化測(cè)試的重要性顯而易見(jiàn),但自動(dòng)化測(cè)試又無(wú)法解決所有問(wèn)題,所以說(shuō)完全依賴自動(dòng)化是不可能的,但完全沒(méi)有自動(dòng)化是萬(wàn)萬(wàn)不能。在軟件開(kāi)發(fā)項(xiàng)目中,重度依賴人力進(jìn)行持續(xù)回歸是一件非??菰锏闹貜?fù)工作。企...
摘要:產(chǎn)生的崩潰測(cè)試用例可能難以分析,因?yàn)槟:郎y(cè)試的行為并不能告訴你關(guān)于軟件內(nèi)部運(yùn)行方式的知識(shí)。模糊測(cè)試向軟件系統(tǒng)提供隨機(jī)輸入。 軟件質(zhì)量保障 專注測(cè)試圈,自動(dòng)化測(cè)試、測(cè)試平臺(tái)開(kāi)發(fā)、測(cè)試新技術(shù)、大廠測(cè)試崗面經(jīng)分享, 可以幫忙內(nèi)推BATJ等大廠!歡迎加VX溝通交流: ISTE1024 測(cè)試同...
摘要:概述是由有贊開(kāi)發(fā)的自動(dòng)化工具,并以此實(shí)現(xiàn)了端和端的核心業(yè)務(wù)的自動(dòng)化。旨在簡(jiǎn)化開(kāi)源工具提供的接口,方便自動(dòng)化測(cè)試用例的設(shè)計(jì)。元素定位自動(dòng)化用例其實(shí)可以分成兩部分,定位元素調(diào)用接口操作該元素。一臺(tái)用于跑自動(dòng)化用例的服務(wù)器。 概述 Bee 是由有贊 QA 開(kāi)發(fā)的 UI 自動(dòng)化工具,并以此實(shí)現(xiàn)了 web 端和 wap 端的核心業(yè)務(wù)的自動(dòng)化。旨在簡(jiǎn)化開(kāi)源工具提供的接口,方便 UI 自動(dòng)化測(cè)試用例...
摘要:輸出結(jié)果需要人工檢查的測(cè)試不是一個(gè)好的單元測(cè)試。為了有效的進(jìn)行單元測(cè)試,需要遵循一定的方法,通常采用路徑覆蓋法設(shè)計(jì)單元測(cè)試用例。 在微服務(wù)架構(gòu)下高覆蓋率的單元測(cè)試是保障代碼質(zhì)量的第一道也是最重要的關(guān)口,應(yīng)該持之以恒。 背景 單元測(cè)試為代碼質(zhì)量保駕護(hù)航,是提高業(yè)務(wù)質(zhì)量的最直接手段,實(shí)踐證明,非常多的缺陷完全可以通過(guò)單元測(cè)試來(lái)發(fā)現(xiàn),測(cè)試金字塔提出者M(jìn)artin Fowler 強(qiáng)調(diào)如果一個(gè)高...
閱讀 1608·2021-11-19 09:55
閱讀 2838·2021-09-06 15:02
閱讀 3627·2019-08-30 15:53
閱讀 1232·2019-08-29 16:36
閱讀 1301·2019-08-29 16:29
閱讀 2352·2019-08-29 15:21
閱讀 679·2019-08-29 13:45
閱讀 2734·2019-08-26 17:15