首頁技術(shù)文章正文

模塊級(jí)別自動(dòng)化測試的經(jīng)驗(yàn)與教訓(xùn)

更新時(shí)間:2018-10-26 來源:黑馬程序員技術(shù)社區(qū) 瀏覽量:

模塊級(jí)別自動(dòng)化測試的經(jīng)驗(yàn)與教訓(xùn)概述

搞了幾個(gè)月的自動(dòng)化測試,結(jié)果不甚理想,這里做一個(gè)簡單的總結(jié)。

為什么要做自動(dòng)化測試呢?因?yàn)槭止y試效率低,找 case、執(zhí)行 case 太費(fèi)時(shí)間。

為什么自動(dòng)化測試前面要加“模塊級(jí)別”呢?因?yàn)橐粋€(gè)系統(tǒng)依賴很多外部系統(tǒng),如果不能有效屏蔽外部環(huán)境的差異,自動(dòng)化測試會(huì)經(jīng)常因?yàn)榄h(huán)境問題而失敗。

原理

這里講的自動(dòng)化測試原理是,在線上環(huán)境錄制測試 case,在線下測試環(huán)境利用錄制的 case 進(jìn)行回放測試。

如何錄制 case?某個(gè)系統(tǒng)對一次請求的一次處理可以認(rèn)為是一個(gè) case,模塊級(jí)別的自動(dòng)化測試要求錄制該系統(tǒng)在處理請求時(shí)的所有對外交互,包括但不限于:

該模塊接收到的請求和對外響應(yīng)信息。該模塊在處理請求過程中,產(chǎn)生的所有下游調(diào)用的請求和響應(yīng)。常見的有:HTTP 請求、DB 請求、Redis 查詢、MQ 輸出、熱更新配置等等。

以下圖為例,有 A、B、C、D 四個(gè)系統(tǒng)模塊,假設(shè)要對模塊 A 做自動(dòng)化測試,需要錄制測試 case。該模塊有一個(gè)接口,接收來自模塊 D 的請求,在處理過程中會(huì)產(chǎn)生對模塊 B、C 的下游調(diào)用。那么在錄制 case 時(shí),不僅要錄制該接口的輸入輸出,也有錄制兩次下游調(diào)用的輸入和輸出。

在請求處理過程中,往往會(huì)出現(xiàn)各種線程的切換、異步調(diào)用,如何將一個(gè)請求的所有對外交互串起來呢?很多公司都有分布式鏈路跟蹤系統(tǒng),會(huì)有一個(gè) traceID 能夠把不同系統(tǒng)的請求串聯(lián)起來,同理也可以利用該 traceID 把單個(gè)系統(tǒng)內(nèi)部的請求處理過程串聯(lián)起來,擁有同一個(gè) traceID 的輸入輸出可以認(rèn)為是屬于同一個(gè) case 的。

case 的錄制思路比較簡單,收集數(shù)據(jù)后存儲(chǔ)到 DB 中即可。case 的回放測試分為兩步:

運(yùn)行測試 case。DIFF 測試結(jié)果,需要進(jìn)行 DIFF 的數(shù)據(jù)包括測試接口的響應(yīng)參數(shù)、下游調(diào)用的請求參數(shù),也就是被測模塊對外輸出的數(shù)據(jù)。
經(jīng)歷的坑本地緩存的 dump 問題

前面說了,錄制 case 時(shí),需要錄制所有的下游調(diào)用的請求和響應(yīng)。對于需要定期更新的本地緩存,也需要 dump 其數(shù)據(jù),如何 dump 呢?dump 查緩存的方法即可,以下面代碼為例,dump 方法 getXXXConfig 的輸入輸出即可。本質(zhì)上是將 getXXXConfig 方法的調(diào)用當(dāng)成了外部請求進(jìn)行錄制。


@Component

public class XXXCache {

    private LoadingCache<String, List<XXX>> xxxCache = CacheBuilder.newBuilder().maximumSize(10000).expireAfterWrite(5, TimeUnit.MINUTES).build(

            new CacheLoader<String, List<XXX>>() {

                public List<XXX> load(String key) {

                    // 省略

                }

            });


    public List<XXX> getXXXConfig(String param) {

        try {

            return xxxCache.get(param);

        }catch (Exception e) {

            logger.error("get xxx error, param:{}", e, param);

        }

        return ListUtils.EMPTY_LIST;

    }

}



性能問題


收集數(shù)據(jù)必定會(huì)耗費(fèi)一些性能,為了減輕對系統(tǒng)的壓力,錄制時(shí)增加了采樣率的概念,只錄制少量數(shù)據(jù)。


多數(shù)情況下,降低采樣率可以解決性能問題,但也有例外。在對接某個(gè)系統(tǒng)時(shí)發(fā)現(xiàn)其有性能問題,CPU 從 20% 漲到了 30%,發(fā)現(xiàn)一次請求里要錄制 5000 條下游調(diào)用,我們將采樣率降低到 10 分鐘錄制一個(gè) case 依然無法解決問題。那么到底是哪里耗費(fèi)性能呢?研究后發(fā)現(xiàn),是在判斷是否收集數(shù)據(jù)的方法里比較耗費(fèi)性能,該方法在每次有下游調(diào)用時(shí)都需要用來判斷是否需要收集數(shù)據(jù)。該方法代碼如下所示,其中有兩點(diǎn)影響性能:


在低并發(fā)的情況下,ConcurrentHashMap 競爭較少影響不大,然而在高并發(fā)情況下競爭較多,性能下降。

inRecordEnv 是一個(gè) volatile 變量,會(huì)引起 CPU 緩存失效,少量影響不大,但是一次請求里 5000 次 CPU 緩存失效影響就大了。

public volatile boolean inRecordEnv;

public ConcurrentHashMap<String, TraceContext> recordingTraceIdMap;


public boolean needRecord(String traceId) {

    if (! inRecordEnv) {

        return false;

    }


    return recordingTraceIdMap.contains(traceId);

}



解決該問題的辦法有兩個(gè):


將是否需要錄制放到線程本地變量中,跟 traceID 一起傳遞,這需要改到分布式鏈路跟蹤組件。

case 錄制一般是選擇集群中的某一臺(tái)服務(wù)器進(jìn)行錄制的,那么修改負(fù)載均衡機(jī)制,降低錄制 case 集群的權(quán)重,即可減輕其壓力。同時(shí),需要稍作處理,讓其他機(jī)器需要不走錄制判斷邏輯,畢竟單單是錄制判斷邏輯就足以影響性能了。

靜態(tài)方法、隨機(jī)數(shù)問題


有一些緩存的查詢用的是靜態(tài)方法,靜態(tài)方法因?yàn)椴槐?Spring 容器托管,是無法使用 Spring AOP 處理的。對于此類方法,如果要 dump 數(shù)據(jù),就必須使用 java instrument 字節(jié)碼修改技術(shù)。


與之類似的還有隨機(jī)數(shù)生成器、UUID 生成,這些隨機(jī)數(shù)往往是某個(gè) Redis 值的 key,也需要去 mock 住才能保證測試成功。


帶異步任務(wù)的請求結(jié)束時(shí)間判斷問題


在業(yè)務(wù)處理中,為了提升處理速度,經(jīng)常會(huì)啟動(dòng)多個(gè)線程同時(shí)處理,處理完畢后再合并請求結(jié)果返回。


在一些特殊情況下,業(yè)務(wù)處理過程中,會(huì)啟動(dòng)異步任務(wù)去計(jì)算,主線程不等待異步任務(wù)結(jié)束便返回結(jié)果。而該異步任務(wù)的結(jié)果會(huì)存在分布式緩存 Redis 中,供下一階段業(yè)務(wù)處理使用。問題產(chǎn)生在原因是,我們需要錄制主線程和異步任務(wù)的數(shù)據(jù),但是難以判斷請求的處理何時(shí)真正結(jié)束。一般都是在接口返回?cái)?shù)據(jù)給調(diào)用方時(shí),就認(rèn)為請求處理結(jié)束了,此 case 的數(shù)據(jù)錄制完成。而這里需要等待異步任務(wù)結(jié)束,才算真正錄制完成。

解決辦法是:新增任務(wù)標(biāo)記 API,用于標(biāo)記異步任務(wù)的啟動(dòng)和結(jié)束,在業(yè)務(wù)代碼中進(jìn)行標(biāo)記。

代碼規(guī)范問題

在業(yè)務(wù)系統(tǒng)中引入模塊級(jí)自動(dòng)化測試時(shí),發(fā)現(xiàn)一些通用的問題:

一些 Bean 缺少默認(rèn)構(gòu)造函數(shù):因?yàn)樯婕暗?Mock 數(shù)據(jù)的反序列化,需要這些 Bean 有默認(rèn)構(gòu)造函數(shù)。traceID 傳遞過程中丟失:因?yàn)殇浿普埱笠蕾?traceID 來判斷是否錄制,所以在線程切換、異步回調(diào)時(shí),必須保證 traceID 的正確傳遞。TimeStamp 問題:一些請求參數(shù)里會(huì)有當(dāng)前時(shí)間的信息,時(shí)間信息在回放時(shí),會(huì)發(fā)生變化,導(dǎo)致測試 DIFF 失敗。解決辦法有兩個(gè),一是測試時(shí)修改系統(tǒng)時(shí)間,此方法存在精確度的問題,二是忽略時(shí)間字段的 DIFF。
總結(jié)

模塊級(jí)自動(dòng)化測試的思路是:在線上環(huán)境錄制 case,通過錄制請求處理過程中的所有對外交互,實(shí)現(xiàn)對外部環(huán)境依賴的解耦。在線下測試環(huán)境回放測試 case,并且 DIFF 對于對外輸出。

在業(yè)務(wù)系統(tǒng)接入過程中,遇到的問題,一些是代碼規(guī)范問題,一些是特殊業(yè)務(wù)邏輯造成的問題。其中,第二類問題嚴(yán)重影響接入效率。

本文版權(quán)歸軟件測試培訓(xùn)學(xué)院所有,歡迎轉(zhuǎn)載,轉(zhuǎn)載請注明作者出處。謝謝!

作者:軟件測試培訓(xùn)學(xué)院
首發(fā):http://www.pantone-color.com.cn/special/testzly/index.html

分享到:
在線咨詢 我要報(bào)名
和我們在線交談!