1. 程式人生 > >我對自動化測試的一些認識

我對自動化測試的一些認識

前言

從2017年初開始,到現在差不多半年多的時間,我這邊投入了一部分精力用於專案的自動化測試建設工作。目前來看收益還是比較明顯的,在這個過程中也加深了對自動化測試的理解,這邊就總結下自己對自動化測試的認識。

首先我想說下在開展自動化前後,我的工作狀況的對比:

  • 去年2016年Q3、Q4,基本上天天處於996甚至997的狀態。大部分時間都花在了功能測試保障、迴歸測試和上線驗證。因為專案在線上有多達7,8個不同的叢集,每一次版本上線為了保險起見,開發會一個叢集一個叢集小心翼翼地上線,所以基本上天天都處於上線的狀態。每次上線,手工測試時間順利的話在半小時左右,如果遇到問題跟開發聯調定位,會達到數個小時。除了時間上開銷很大之外,每次上線帶來的精神上的壓力其實更嚴重,非常害怕哪個使用者半夜跳出來反饋說排程有Bug。

  • 在開展了自動化測試之後,現在我給“日常版本迭代測試”只預估了25%的工作量。任意叢集上線,我只要負責點一下Jenkins的“開始構建”按鈕,即可完成驗收。如果有出現用例失敗,會自動傳送郵件告知開發。

顯然,目前的工作狀態要好很多,是因為專案工作量減少了嗎?顯然不是。其實今年以來整個專案組在研發的投入要比去年更多,工作量只會比以前更重,還要兼顧多個私有化部署的客戶的驗收和日常測試保障工作,工作量肯定是隻增不減的。之所以能有更多的時間空餘出來做其它更多維度的事情,這一切都得益於“自動化測試”的幫助,它極大地解放了我的手工測試時間,同時更加提升了上線的信心。

1. 需求和目標

在我開展自動化測試之前,其實該專案以前的測試人員也已經寫了很多的介面測試用例,但是大多數用例處於“半癱瘓”狀態,在CI上無人維護(聽說起初是有人維護的,但是後來用例多了,維護的人每次花很長時間去定位問題,結果卻發現大部分的問題都是環境問題導致,花了半天時間定位卻沒什麼收益,久而久之便不想去維護)。看起來,自動化似乎並沒有什麼收益,反而維護用例會造成額外的工作負擔。

我覺得,其實自動化測試跟其它任何一種測試型別(比如異常測試、穩定性測試、效能測試等)都是類似的,它也是一種測試型別而已。在開展測試之前,我們首先必須要明確自動化測試的需求是什麼,要解決什麼樣的問題。

1.1 讓“自動化”代替“手動”

在我看來,初期的自動化測試,我的目標很明確,我就是要讓“自動化”代替“手動”,讓自動化真正地跑起來,凡是“自動化”跑過的內容,我絕不再去手工重複執行一遍。這樣至少我有一個很明確的收益:每完成一條自動化用例,我減少了一條手工用例的執行時間。

必須要提醒的是,讓“自動化”完全替代“手動”,其實對自動化用例的穩定性、容錯都有一定的要求。你要花一定時間去思考用例執行過程中的異常場景,是否足以充分替代手工測試。因此,我在增加用例的時候都會非常謹慎,確保用例集是穩定100%通過的前提下才會增加新的用例。

對於正常情況下(排除環境、開發程式碼的問題)有時100%通過,有時90%通過的自動化用例集,我覺得它的作用和參考價值為0。正常的用例集就應該是100%通過的。

1.2 讓“迴歸”自動化

上節說了讓“自動化”替代“手動”,每完成一條自動化用例都是有明顯收益的。那如何讓收益最大化呢,當然是讓每次迴歸或上線驗證“不得不”執行的用例優先自動化。如果完成了迴歸用例集的全部自動化,那我就可以用它來替代我的日常回歸,和上線迴歸工作,極大地釋放我的手工驗證時間。

這裡必須要指出的是,我跟的專案其實是一個對系統穩定性的要求要高於新功能的引入的一個後臺專案,所以它的核心功能是比較固定的,其實大多數後臺專案也是類似的,核心功能聚合、對系統的穩定性要求高。這就需要保障系統的核心功能完善。所以我們可以先將“核心功能”的驗證完成自動化。

1.3 不要讓環境成為瓶頸

前面說了,舊的用例集在維護的過程中給測試人員增加了很多額外的負擔,到最後發現很多都是環境的問題。當時的情形就是專門搭建了另一套測試環境專門用於自動化測試,而大資料的後臺環境搭建和維護非常的複雜,如果同時維護多套環境,難免會在一些元件升級的過程中出現遺漏,導致環境不同步。因此,我們的自動化測試用例前期完全可以直接在功能測試環境執行,因為功能測試環境肯定是會一直隨著版本的迭代向前不斷更新的。

2. 技術選型

在明確了目標後,要開始技術選型。常見的自動化測試型別,包括

  • 介面自動化
  • UI自動化
  • 基於shell互動命令執行的自動化

此外,不屬於測試範疇,但是也可以實現自動化、釋放手工時間的還有

  • 資料準備自動化
  • 環境編譯、部署、打包自動化
  • 穩定性測試/效能測試結果指標獲取、校驗自動化
  • 機器資源監控、報警自動化
  • 其它所有手工重複執行的操作

在開始自動化之前,首先要分析專案的架構和狀況。對於一個後端的服務,它如果是純粹以介面的形式提供給其它元件去呼叫,那可以採取“介面自動化”;對於一個Web產品,如果前後端都在測試的保障範圍,而且前端頁面相對比較穩定,可以考慮採用“UI自動化”(此時介面自動化其實已經不足以保障產品的端到端功能);對於更後端的元件,如果想測試元件自身的基礎核心功能,可以採用“基於shell互動命令執行的自動化”,通過自動化指令碼的方式封裝shell命令的呼叫。

此外,有些人可能還會執著於程式語言的選擇,是用Java還是Python還是Shell,或者其它語言等等。這個我覺得其實沒有定論,可以根據自己對語言的偏好和熟練程度,但是必須要考慮團隊成員的普遍技術棧,因為後期可能其他人來接手這個專案時需要代替你去維護測試工程。通常來說,測試框架的選擇(不管是介面自動化、UI自動化)推薦使用Java的TestNG框架;對於簡單的基於命令列執行的自動化指令碼的編寫推薦使用Shell(Shell非常地強大);對於稍複雜的一些自動化的指令碼的編寫,推薦使用Python,在Python中可以非常方便地封裝Shell命令,同時Python區別於Shell的一個特性就是它支援面向物件的封裝,可以將一些物件封裝在特定的類中,增加程式的可讀性和健壯性。

這裡再插一段題外話:有些人可能會疑惑,現在其實有很多介面測試平臺,測試人員可以直接在平臺上完成介面測試,在選型時怎麼抉擇?——這裡我不評價哪種方式更好,只想說下自己的看法:我覺得兩種其實各有各的好處:

  • 編寫程式碼的方式:

優點:提升自己的編碼能力,問題定位能力,具備更高的靈活性和可操作性。 缺點:結果展示不直觀,不易於協作。其他人維護程式碼困難,難以推動開發執行。

  • 介面平臺的方式:

優點:簡便,上手容易,可以在專案組間很好的協作和維護,測試記錄和結果一目瞭然。 缺點:離開了平臺,可能又要回歸手動。

對於測試人員而言,如果有精力和時間的話,我建議是兩種都要掌握,甚至是自己去開發介面測試平臺的能力。

3. 自動化實施過程

目前我跟的專案裡已經實現自動化的內容包括:基於介面的場景迴歸自動化測試、編譯部署過程自動化、Jacoco覆蓋率統計並接入CR平臺(程式碼變更分析平臺)的自動化、對外/上線打包釋出的自動化、穩定性測試結果校驗的自動化。

下面著重介紹下專案的介面自動化框架的搭建和設計過程。

3.1 準備工作

老生常談,開始自動化前,我仍然想再次強調一定要明確自己的需求是什麼。在我的專案裡,我的需求主要有以下幾點:

  • 同一份程式碼可以在多個叢集執行
  • 各個叢集的測試資料相互獨立,不會互相影響
  • 可以方便地與資料庫進行互動
  • 當用例執行出錯時,有詳細的日誌幫助定位
  • 較好的可維護性和叢集擴充套件性。

3.2 框架搭建

3.2.1 環境搭建

環境搭建時,主要用了以下工具:

  • Git:管理程式碼工程
  • TestNG:作為測試框架
  • Maven:管理依賴包
  • Log4j:管理日誌
  • Hibernate:實現資料庫互動
  • HttpClient:實現請求傳送

之所以沒有用MyBatis,覺得相對來說,MyBatis是一個半ORM的框架,它需要自己額外維護一份sql對映檔案,而Hibernate是全ORM的,可以省去這一步。關於它倆的比較,大家可以參考下知乎的一篇文章:MyBatis和Hibernate的對比。對於JDBC的方式,當然它也可以訪問資料庫,只不過相對來說,使用ORM框架可以更貼近面向物件的程式設計方式。

3.2.2 不同叢集配置管理

在實現過程中,因為不同的叢集會有不同的配置,比如webserver host、登陸後臺webserver的使用者名稱/密碼、公共賬號資訊、資料庫資訊等等。為了讓一份程式碼可以在不同叢集去共用,就必須把這些配置資訊從程式碼中剝離出來。可以用配置檔案的形式來統一管理叢集的配置資訊,如圖所示:

配置檔案管理

每個檔案代表一個叢集的配置。在程式碼中可以通過java.util.Properties類讀取配置檔案的方式載入各項配置資訊:

/**
 * 根據指定的配置檔名,初始化配置
 * @param configFile
 * @throws IOException
 */
public PropertiesUtil(String configFile) throws IOException{
    this.configFile =DEFAUL_CONFIG_FILE_DIRECTORY + configFile;
    InputStream fis = new FileInputStream(this.configFile);
    props = new Properties();
    props.load(fis);
    //關閉資源
    fis.close();
}

/**
 * 根據key值讀取配置的值
 * @param key key值
 * @return key 鍵對應的值 
 * @throws IOException 
 */
public String readValue(String key){
    return  props.getProperty(key);
}

/**
 * 讀取properties的全部資訊
 * @throws FileNotFoundException 配置檔案沒有找到
 * @throws IOException 關閉資原始檔,或者載入配置檔案錯誤
 * 
 */
public Map<String,String> readAllProperties(){
    //儲存所有的鍵值
    Map<String,String> map=new HashMap<String,String>();
    Enumeration<?> en = props.propertyNames();
    while (en.hasMoreElements()) {
        String key = (String) en.nextElement();
        String property = props.getProperty(key);
        map.put(key, property);
    }
    return map;
}

到這裡,解決了配置讀取的問題,還需要解決程式碼執行時如何讓它自己去選擇正確的叢集配置檔案的問題。我是將選擇配置檔案的邏輯全部封裝到了一個工廠類BaseConfigFactory.java中,在實際測試使用時,我只需要通過工廠類的靜態方法BaseConfigFactory.getInstance()去獲取想要的配置資訊,而不需要關心它到底是如何去選擇正確的配置檔案的。工廠類的實現可以參考:

public class BaseConfigFactory {
    private static final String testEnv= System.getenv("TEST_ENV") == null ? "null" : System.getenv("TEST_ENV");
    private static Logger logger = Logger.getLogger(BaseConfigFactory.class);
    private static BaseConfig baseConfig;
    private static HashMap<String, String> clusterConfigMap;
    public static synchronized BaseConfig getInstance(){
        if (null == baseConfig){
            PropertyConfigurator.configure("log4j.properties");
            initMap();
            setupConfig();
        }
        return baseConfig;
    }
    
    
    public static void initMap(){
        clusterConfigMap = new HashMap<>();
        clusterConfigMap.put("TEST-BJ", "test-bj.properties");
        clusterConfigMap.put("ONLINE-BJ", "online-bj.properties");
        clusterConfigMap.put("ONLINE-XS", "online-xs.properties");
        clusterConfigMap.put("ONLINE-LT", "online-lt.properties");
        clusterConfigMap.put("ONLINE-BEIJING", "online-beijing.properties");
        clusterConfigMap.put("ONLINE-HD", "online-hd.properties");
        clusterConfigMap.put("null", "test-local.properties");
    }
    
    public static void setupConfig(){
        logger.info("TEST ENV: " + testEnv);
        String propertyFile = clusterConfigMap.get(testEnv);
        logger.info("Using '" + propertyFile + "' as property file.");
        baseConfig = new BaseConfig(propertyFile);      
    }

}

即,將所有的叢集的配置放入到一個Map中,然後通過讀取環境變數TEST_ENV的值來選取具體的叢集配置檔案clusterConfigMap.get(testEnv)。

3.2.3 log4j日誌管理

良好的日誌輸出是幫助定位問題的關鍵環節,尤其是定位伺服器上執行時出現的問題。這邊貼一個log4j的配置:

### set log levels ###
log4j.rootLogger = debug, stdout, D, E

### 輸出到控制檯 ###
log4j.appender.stdout = org.apache.log4j.ConsoleAppender 
log4j.appender.stdout.Target = System.out
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy/MM/dd HH:mm:ss.SSS Z} %p [%c{1}] [Thread-%t] %m%n

### 輸出到日誌檔案 ###
log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
log4j.appender.D.File = logs/console.log
log4j.appender.D.Append = true
##輸出Debug級別以上的日誌##
log4j.appender.D.Threshold = INFO  
log4j.appender.D.layout = org.apache.log4j.PatternLayout
log4j.appender.D.layout.ConversionPattern=%d{yyyy/MM/dd HH:mm:ss.SSS Z} %p [%c{1}] [Thread-%t] %m%n

### 儲存異常資訊到單獨檔案 ###
log4j.appender.E = org.apache.log4j.DailyRollingFileAppender
##異常日誌檔名##
log4j.appender.E.File = logs/error.log
log4j.appender.E.Append = true
##只輸出ERROR級別以上的日誌##
log4j.appender.E.Threshold = ERROR
log4j.appender.E.layout = org.apache.log4j.PatternLayout
log4j.appender.E.layout.ConversionPattern=%d{yyyy/MM/dd HH:mm:ss.SSS Z} %p [%c{1}] [Thread-%t] %m%n

##Hibernate日誌級別設定
log4j.logger.org.hibernate.ps.PreparedStatementCache=WARN
log4j.logger.org.hibernate=ERROR

# Changing the log level to DEBUG will result in Hibernate generated
# SQL to be logged.
log4j.logger.org.hibernate.SQL=ERROR

# Changing the log level to DEBUG will result in the PreparedStatement
# bound variable values to be logged.
log4j.logger.org.hibernate.type=ERROR

該配置將INFO級別和ERROR級別的日誌分別定位輸出到不同的檔案,且日誌檔案會按照日期進行自動歸檔,輸出的格式包含了日誌的日期、級別、類資訊、執行緒資訊、日誌內容等。

一般情況下,對於介面測試,當介面測試用例失敗時,我們要列印的日誌包括:請求的url、引數、方法、實際響應、期望響應等等。

3.3 分層設計、解耦

首先看一下專案的工程目錄:

專案工程目錄

可以看到,專案中包含了多個package,各個package的作用已經在圖片中標示了。以前好多測試人員的習慣是將api程式碼的呼叫、測試方法的編寫、data Provider的編寫、測試資料的構造全部寫在一個類檔案中,這樣做其實會有幾個問題:

  • 可讀性差
  • 程式碼複用性低
  • 維護性差
  • 難以除錯
  • 耦合帶來的其它各類問題

此外,如果不同叢集的測試資料不同,會有大量的if判斷,結果是災難性的。

下面以一個用例為例,展示程式碼的結構:

測試api:

public class ScheduleApi extends BaseAzkabanApi{
    ...
    ...
    /**
     * 使用預設公共賬號、email、失敗策略、sla報警郵箱新增正常排程。
     * @param projectName
     * @param flow
     * @param projectId
     * @param scheduleTime
     * @param scheduleDate
     * @param period
     * @return
     */
    public ResponseCode addNormSched(String projectName, String flow, String projectId, String scheduleTime, String scheduleDate,String period){
        return scheduleFlow(projectName, flow, projectId, scheduleTime, scheduleDate, defaultProxyUser, defaultProxyEmail, period,  defaultSlaEmail);
    }
    ...
    ...
}

測試程式碼test:

@Test(singleThreaded=true)
public class ScheduleTest{
    ...
    ...
    /**
     * 新增正常排程
     * @param projectName
     * @param flow
     */
    @Test(priority=1, dataProvider="addNormSched", dataProviderClass=ScheduleDataProvider.class, testName="1410356")
    public void addNormSched(String projectName, String flow, String expectedStatus, String hasScheduleId, String message){
        ResponseCode rc= scheduleApi.addNormSched(projectName, flow);
        Assert.assertEquals(rc.getStatus(), expectedStatus, message+rc.getDebugInfo("返回結果中的狀態status對應值"));
        Assert.assertEquals(rc.hasProperty("scheduleId"), Boolean.parseBoolean(hasScheduleId), message+rc.getDebugInfo("返回結果中是否包含scheduleId"));
    }
    ...
    ...
}

測試用例dataProvider:

public class ScheduleDataProvider {
    @DataProvider(name = "addNormSched", parallel=true)
    public static Object [][] addNormSched(){
        return new Object[][]{
            ScheduleTestData.validNormSchedule,
            ScheduleTestData.notExistedProject,
            ScheduleTestData.notExistedFlow
        };
    }
    ...
    ...
}

測試資料testdata:

public class ScheduleTestData extends BaseTestData{ 
    ...
    ... 
    //Testdata for addNormSched
    public static Object[] validNormSchedule={VALID_PROJECT_NAME, VALID_NORMAL_SCHEDULE_FLOW, "success", "true", "設定有效的正常排程"};
    public static Object[] notExistedProject={NOT_EXIST_PROJECT_NAME, VALID_NORMAL_SCHEDULE_FLOW, "error", "false", "不存在的project"};
    public static Object[] notExistedFlow={VALID_PROJECT_NAME, NOT_EXIST_FLOW_NAME, "error", "fasle", "不存在的flow"};
    ...
    ...
}

可以看到,用例的測試程式碼test類是非常簡潔的,只要呼叫api類封裝的介面,然後進行assert判斷即可。

關於測試資料,將dataprovider與testdata進行分離,也是為了後續可能會靈活地調整下架用例,只需要去除dataprovider類中的用例行即可,而testdata中的資料仍然可以留著複用。

另外,前面提到了不同叢集測試資料的管理。再介紹下我這邊的實現方式:

  • 不同測試類使用的公共資料,存放於BaseTestData基類中,讓其它testdata類繼承於基類
  • 不同叢集可以共用的資料,儘量共用,以常量的方式儲存於testdata類中
  • 不同叢集無法共用的資料,統一存放於特定的json檔案管理

關於json檔案管理資料,其實跟配置檔案的管理類似,如下圖所示:

json資料目錄

History.json:

{           
    "validTotalFetch":{
        "key":"",
        "beginTime":"2017-06-30%2015:30",
        "endTime":"2017-06-30%2015:50",
        "expectedTotal":"7"
    },
    
    "validImmediatelyFetch":{
        "key":"instant_execute_job",
        "beginTime":"2017-06-30%2013:30",
        "endTime":"2017-06-30%2013:40",
        "expectedTotal":"1"
    },
    
    "validScheduledFetch":{
        "key":"online_schedule_job",
        "beginTime":"2017-06-30%2014:30",
        "endTime":"2017-06-30%2014:40",
        "expectedTotal":"2"
    }
}

3.4 改進與提升

在自動化的實施過程中,還遇到了一些問題可能對其它專案也會有一定的借鑑意義。這邊羅列下幾個我覺得比較有意思的問題。

3.4.1 webserver高可用的支援

我們的後臺webserver是支援高可用的,所以每次運維上線後webserver的host可能會發生變化,以及在服務執行過程中也可能會發生webserver切換。如果每次去手動調整自動化用例的配置資訊,是一件非常麻煩的事情。

解決的方式就是在配置檔案中,將主從webserver的host都填寫進去,在測試過程中,如果發生請求失敗,則允許切換一次host。

3.4.2 用例併發執行

由於我們的一部分用例是非同步的場景用例,需要執行一個數據開發的任務,然後等待其執行完成。這些用例的執行比較費時,如果順序執行的話會消耗非常多的時間。因此可以通過併發執行測試的方式,解決用例耗時的問題。關於TestNG的併發可以參考這篇文章:《簡單聊聊TestNG中的併發》

3.4.3 單例模式解決session問題和host重複切換問題

  • 問題1: Azkaban的每個介面,都需要一個必傳引數seesion。這個session可以通過/login介面獲取。如果每個介面在執行的時候都去呼叫一次/login介面重新獲取session,就會顯得很冗餘,也可能導致舊的session失效。

  • 問題2: 上述提到的對webserver高可用的支援,當多條用例並行執行時如果同時去切換host,可能會造成host切換回原來的不可用host。

對於問題1,可以將session作為單例的方式進行儲存。

對於問題2,可以借鑑單例模式的“雙重檢查”思想,對切換host的程式碼進行部分同步,在防止host重複切換的同時,不會降低httpclient請求的併發性。關於單例模式的應用可以參考這篇KS文章:《“單例模式”學習及其在優化介面自動化測試程式碼中的實踐》

3.4.4 “變”與“不變”

其實這也是所有設計模式的基本思想,即區分自動化測試中的“可變因素”和“不變因素”。我覺得ycwdaaaa大神(飛哥)有兩句話是非常棒的:

  • 封裝"一切"可能的可控的變化因素
  • 為了穩定使盡"一切"手段

4. 結合研發過程的應用

上面介紹了一些自動化的實施過程,這邊再介紹下實施之後在專案研發過程中的應用。

目前在專案中,主要有以下幾方面的應用。

(1)提測後的自動化迴歸驗收

下圖是專案的一條持續整合pipeline。在開發提測後,我會自動化地完成以下事情:

  • 編譯程式碼
  • 將服務部署到各個機器,並完成Jacocod Agent的部署
  • 執行靜態程式碼檢查
  • 執行介面測試
  • 完成覆蓋率統計
  • 將覆蓋率統計資料接入到CR平臺

當自動化用例全部執行通過時,說明系統的核心功能迴歸沒有問題,然後開始版本的細粒度功能的測試。

持續整合pipeline

(2)Bug修復後的迴歸驗收

在測試過程中,開發肯定會經常修復bug重新提交程式碼,每次有程式碼重新提交時,我都可以一鍵完成部署、測試、覆蓋率統計。

(3)上線後的迴歸驗證

目前,專案的上線驗證已經完全由自動化驗證來替代。

(4)作為開發冒煙的一部分(未完成)

目前已經跟開發達成一致,開發非常歡迎將自動化用例接入到開發環境,用於他們每次變更時的環境正確性驗證,可以儘早幫助他們發現研發過程中出現的問題。並且在提測前,只有100%通過自動化測試才可以進行提測。

(5)線上監控

目前各個線上叢集,都部署了自動化測試用例,這部分用例會每隔4小時執行一次。用於確保線上環境的穩定性。從效果上來看,線上監控的成效是非常明顯的,提前發現了很多叢集的延遲問題,環境問題等,讓開發可以及時地收到報警,瞭解線上叢集的情況。

(6)關於持續整合

可能有人會發現,上述的執行過程其實不是真正意義上的持續整合,真正意義上的持續整合應該是:每次開發提交程式碼,自動觸發構建。

必須要承認的是,確實是如此。但是不管怎麼樣,我覺得可以先從優化測試工作量的角度慢慢去推開整個流程,其實業界目前也並沒有確切的定論說只有持續整合才是最佳的實踐。相反,一味地持續整合可能會增加我們的維護成本。只要我們能切實提升自己的工作效率,達到目的就可以了。

5. 成效

當自動化做的比較完善後,你真的會發現:生活原來可以變得如此簡單美好。

自動編譯部署:測試過程中開發修復bug提交程式碼是非常頻繁的,每次的手動編譯部署可能都會耗費十幾分鍾,並且測試人員的關注點還不能離開。

自動打包釋出:從這個版本開始,所有叢集的上線都會統一使用QA釋出的包。這樣減少了以前每次上線時,開發運維人員要花費大量的時間逐一去拉取各個叢集的程式碼再進行編譯、部署。一鍵的打包釋出,可以在上線前就提前準備好各個叢集的上線包,開發只需呼叫部署指令碼去獲取這些包,然後替換就可以完成上線。此外,自動打包釋出的方式極大減少了運維上線時漏操作的風險。

自動化迴歸測試:以前一次迴歸測試,需要QA持續地投入超過30分鐘。現在通過一鍵執行,程式會自動地執行,時間控制在5分鐘以內。且QA可以將注意力放到其它事情上。

自動化完成穩定性測試結果的校驗:從前執行完穩定性測試,需要對著資料庫的一大片資料進行人肉地校驗。會耗費一個下午大半天的時間,甚至還是有遺漏。通過指令碼自動校驗,1分鐘內就可以出結果報告。

這裡再提一下UI自動化。很多人會對UI自動化有看法,覺得投入產出比不明顯、維護成本高。我認為UI自動化跟介面自動化其實沒有區別,都是功能迴歸的一種形式而已,選擇哪種自動化的型別應該取決於專案的實際情況需要。另外,UI自動化的維護成本目前一個季度做下來看,真的沒有比介面自動化要高,關鍵還在於自動化的設計上是不是做的易於維護。

6. 展望

可以看到以上的自動化都是基於環境的穩定可用為前提的。之所以沒有獨立分配一套環境用於自動化測試,也是因為環境維護的成本較高。但是,基於測試人員的增加,測試型別的豐富(異常、效能),在一套環境上執行所有測試顯然會出現相互影響的問題。因此,如果能將測試環境搭建docker化,通過維護docker映象的方式,自動化地使用docker映象快捷地部署一套新的完整測試環境可以極大地提高我們的測試效率。

最後的最後,發自真心地希望圈中的前輩大神能給予我一些參考意見和指導,指出我的不足之處。謝謝!