關於介面測試自動化的總結與思考

阿里巴巴雲原生發表於2022-06-27

近期看到阿里雲效能測試 PTS 介面測試開啟免費公測,本著以和大家交流如何實現高效的介面測試為出發點,本文包含了我在介面測試領域的一些方法和心得,希望大家一起討論和分享,內容包括但不僅限於:

  • 服務端介面測試介紹
  • 介面測試自動化介紹
  • 介面測試自動化實踐
  • 關於介面測試自動化的思考和總結

服務端介面測試介紹

什麼是服務端?

一般所說的服務端是指為使用者在 APP 或 PC 使用的網際網路功能提供資料服務的背後的一切。以天貓精靈智慧音響系列的產品鏈路為例,服務端便是閘道器(包括閘道器在內)之後的鏈路。

 title=

什麼是介面?

官方點說,是計算機系統中兩個獨立的部件進行資訊交換的共享邊界。通俗點說,就是服務端對外提供資料服務最常用的資訊交換方式。提供資料服務的服務端是個可大可小的機構,做的事大多不止一件,它做了這麼多事,最終的目標是給 APP 或其它呼叫方使用,於是服務端就派出了幾個代表,比如 API 1 負責提供使用者資訊,API 2 負責提供裝置資訊,API 3 負責提供播放的音訊資訊等等。同事,服務端規定好跟 API 1 通訊的接頭暗號是 param1,param2…,跟 API 2 通訊的接頭暗號是 param3,param4…,而 params 就是介面引數,就是用來告訴服務端你要什麼服務,具體的要求是什麼。介面一般由三個部分組成:協議、地址及引數。

什麼是介面測試?

一般講的介面測試指的是對某個給定介面進行功能測試,輸入不同的引數時,介面返回值是否正確。下圖是經典的測試金字塔模型。

 title=

在這個模型中,越往下比例會佔的越高,也就是說在一個產品測試中,單元測試比例是最高的,依次是介面測試和UI自動化測試,最頂端是人工測試部分。服務端介面測試在中部,承上啟下,由此可見其重要性。

為什麼要做介面測試?

一般做介面測試有如下原因:

  • 介面是服務端對外提供資料服務最常用的資訊交換方式,介面大部分內容都是資料,通過資料對比我們可以推測到系統的邏輯,測介面其實也就是測邏輯。
  • 介面測試相對容易實現自動化,也容易實現持續整合,且相對 UI 自動化也比較穩定,可以減少人工迴歸測試人力成本與時間,縮短測試周期,支援後端快速發版需求。

如何做介面測試?

前面提到,介面是由這幾個組成部分:介面地址、請求協議、請求引數和預期結果。測試介面的步驟一般步驟是:傳送請求->解析結果->驗證結果

簡單來說,介面測試就是參照介面文件,呼叫介面,看結果的返回是否跟文件說明一致;另外,再測試一下介面對異常邏輯的處理比如非法引數或邊界值。

深入來說,介面測試的關注重點在於:

一、介面的資料邏輯是否正確。我們需要充分理解介面的功能,內部是什麼樣的資料邏輯,它與上下游交換了那些資訊或資源,不單純地停留在引數呼叫和程式返回的表象資料。通俗地說,就是要知道這個介面是幹什麼用的,用到哪裡,每次呼叫會發生什麼,然後去檢驗改發生的有沒有發生。

二、介面對於異常引數的處理機制與上下游服務的容錯。如下圖所示,被測介面 A 依賴上游服務 A,那麼服務 A 異常的時候被測介面是否很好的容錯就很重要,否則服務掛起或宕掉都是有可能的。另外,作為服務提供方介面 B,應當要充分相容不同的使用場景、或不同版本的呼叫方的使用,不能為了服務 E 做的需求,除了 E 其它的服務使用者都用不了了。總的來說,原則就是“上游不可靠,下游要相容”。

 title=

介面測試自動化介紹

什麼是介面測試自動化?

介面測試自動化,簡單來講就是功能測試用例指令碼化,然後執行指令碼,產生一份視覺化測試報告。

為什麼要做介面測試自動化?

不管什麼樣的測試方式,都是為了驗證功能與發現 bug。那為什麼要做介面測試自動化呢?一句話概括就是是為了節省人力成本。具體來說,包括以下幾點:

  • 減輕自己工作量,把測試從枯燥的重複勞動的人工測試中解放出來;
  • 協助手工測試完成很難模擬或無法模擬的的工作;
  • 提高工作效率,比如測試環境的自動化編譯、打包、部署、持續整合甚至持續交付等。
  • 協助定位問題,比如介面層發現問題了,可以通過新增的 traceID 定位到日誌錯誤或錯誤程式碼行,
  • 儘早發現 Bug,自動通知測試人員。一旦發現問題,立即通知測試人員,快速高效。

介面測試自動化的規範

這裡結合我平常在做介面測試時的一些經驗,總結了一些介面測試自動化的規範,拋磚引玉,歡迎大家補充。

  • 文件準備

磨刀不誤砍柴工,準備好分詳細的介面相關文件能夠幫助後續介面自動化測試工作的高效展開。相關文件包括但不限於一下內容:

1、 《需求文件》 ,明確定義了:介面背後的業務場景,即該介面是幹什麼用的,用到哪裡,每次呼叫會發生什麼等;

2、 《介面文件》 ,明確定義了:介面名,各個入參值,各個返回值,和其他相關資訊;

3、 《UI 互動圖》 ,明確定義了:各單頁面需展示的資料;頁面之間的互動等;

4、 《資料表設計文件》 ,明確定義了:表欄位規則、表 N 多 N 關係(一對一、一對多、多對多)等;

務必和相關需求方確認好文件中的資訊是可靠且最新的,只有依賴可靠的文件才能設計出正確詳盡的介面用例,才能得到最正確的結果。

  • 明確介面測試自動化需要的功能

1、校驗(斷言)

測試斷言是自動化測試中的測試通過條件,用於判斷測試用例是否符合預期。所以支援對返回值校驗是一個必須的功能。

2、資料隔離

資料隔離就是指具體的請求介面、引數、校驗等資料做到與程式碼相隔離,便於維護,一旦需要調整介面用例、新增介面用例時可很快速的找到位置。隔離的另一個好處就是可複用,框架可以推廣給其他團隊,使用者可以使用相同的程式碼,只需要根據要求填寫各自用例即可測試起來。

3、資料傳遞

做到資料隔離可維護後,資料傳遞是另外一個更重要的需求。介面測試時,首先我們會實現單介面解耦,後續按照業務場景組合多個介面。而資料傳遞是則是組合多個介面的必要條件,它讓介面用例之間可以做到向下傳參。舉個例子,我們通過裝置資訊查詢介面查詢到當前天貓精靈音響的裝置資訊,該介面會返回一個 UUID,接下來我們要通過使用者資訊查詢介面去查詢當前裝置繫結的使用者資訊,此時第二個介面的請求資料是需要從第一個介面用例中的返回中提取的。

4、功能函式

實際的業務場景測試會需要各種輔助功能的支援,比如隨機生成時間戳,請求 ID,隨機的手機號碼或位置資訊等等,此時我們就需要程式碼可以支援做到識別對應關鍵字時可以執行對應的功能函式進行填充。

5、可配置

目前測試環境包括但不限於日常、預發一、預發二、線上等等,因此用例不單單隻能在一個環境上執行,需要同一份介面用例可以在日常、預發、線上等多個環境都可以執行。所以框架需要做到可配置,便於切換,呼叫不同的配置檔案可以在不同的環境執行。6、日誌日誌包含執行的具體執行介面、請求方式、請求引數、返回值、校驗介面、請求時間、耗時等關鍵資訊,日誌的好處一來是可以便於在新增用例有問題時快速定位出哪裡填寫有問題,二來是發現 bug 時方便向開發反饋提供資料,開發可以從觸發時間以及引數等資訊快速定位到問題所在。

7、視覺化報告

用例執行後,就是到了向團隊展示結果的時候了,一個視覺化的報告可以便於團隊成員瞭解到每次自動化介面用例執行的成功數、失敗數等資料。

8、可持續整合

對於已經有測試用例並測試完成的介面,我們希望能夠形成迴歸用例,在下一個版本迭代或上線之前,通過已有用例進行一個迴歸測試,確保新上線的功能不影響已有功能。因此,這就需要介面自動化測試是可持續整合的而不是一次性的。

  • 介面測試自動化框架選型

結合我們對介面測試自動化框架的需求及目前市場上的很多測試工具的特點,總結成下表:

 title=

這裡簡單列舉一下:

1、fiddler

fiddler 是一個 HTTP 協議除錯代理工具,Web 和手機測試都會用到,同時也支援介面測試。它能夠記錄並檢查所有你的電腦和網際網路之間的 http 通訊,設定斷點,檢視所有的“進出”Fiddler 的資料(指 cookie,html,js,css 等檔案)。

2、postman

它是 Google 開發的一個外掛,安裝在 Chrome 瀏覽器上,能支援不同介面測試請求,可以管理測試套件和自動化執行。弱點是自動化斷言功能不強大,不能和 Jenkins、程式碼管理庫進行持續整合測試。

3、wireshak

這是一款抓包工具,支援 TCP、UDP、HTTP 等協議。如果做底層網路資料測試,一般都需要用到它,但是用作介面測試,它就有點不友好。因為重新整理資料太快,不好定位每個操作對應的介面。

4、soupUI

soapUI 是一個開源測試工具,通過 soap/http 來檢查、呼叫、實現 Web Service 的功能/負載/符合性測試。該工具既可作為一個單獨的測試軟體使用,也可利用外掛整合到 Eclipse,maven2.X,Netbeans 和 intellij 中使用。把一個或多個測試套件(TestSuite)組織成專案,每個測試套件包含一個或多個測試用例(TestCase),每個測試用例包含一個或多個測試步驟,包括髮送請求、接受響應、分析結果、改變測試執行流程等。該工具能夠支援介面自動化測試和介面效能測試,也支援和 Jenkins 做持續整合測試。

5、Java 程式碼做介面測試

為什麼要用程式碼做介面自動化測試呢?一些工具功能是有限制,很多公司需要一些特定的功能,工具不支援,只好用程式碼進行開發。一般用 Java 做自動化測試,主要利用 httpclient.jar 包,然後利用 JUnit 或者 TestNG 這樣的單元測試工具,進行測試用例的開發,接著在 Jenkins 或我們的 aone 上建立一個 job,進行持續整合測試。

6、Python 程式碼做介面測試

和 Java 一樣,用 Python 做介面測試,可以利用一個功能強大的第三方庫 Requests,它能方便地建立介面自動化用例。Python 下的單元測試框架,一般採用 unittest。生成測試報告,一般選擇 HTMLTestRunner.py。同樣,可以結合 Jenkins 做持續整合測試。

介面測試自動化實踐

TestNG 與 Junit 對比

  • 綜合性對比

我在日常測試工作中,使用的比較多的自動化測試工具是 Java 程式碼做介面測試,這裡先介紹下我對單元測試工具 TestNG 和 Junit 的對比。先用一張表格總結一下他們的特點對比。

 title=

TestNG 與 JUnit 的相同點如下:

1、都有註解,即都使用 annotation,且大部分 annotation 相同;

2、都可以進行單元測試(Unit test);

3、都是針對 Java 測試的工具;

TestNG 與 JUnit 的不同點如下:

1、TestNG 支援的註解更豐富,如@ExpectedExceptions、@DataProvider 等;

2、JUnit 4 中要求@BeforeClass、@AfterClass 方法宣告為 static,這就限制了該方法中使用的變數必須是 static。而 TestNG 中@BeforeClass 修飾的方法可以跟普通函式完全一樣;

3、JUnit 只能使用 IDE 執行,TestNG 的執行方式有:命令列、ant 和 IDE;

4、JUnit 4 依賴性非常強,測試用例間有嚴格的先後順序。前一個測試不成功,後續所有的依賴測試都會失敗。TestNG 利用@Test 的 dependsOnMethods 屬性來應對測試依賴性問題。某方法依賴的方法失敗,它將被跳過,而不是標記為失敗。

5、對於 n 個不同引數組合的測試,JUnit 4 要寫 n 個測試用例。每個測試用例完成的任務基本是相同的,只是方法的引數有所改變。TestNG 的引數化測試只需要一個測試用例,然後把所需要的引數加到 TestNG 的 xml 配置檔案中或使用@DataProvider 方式注入不同的引數。這樣的好處是引數與測試程式碼分離,非程式設計師也可以修改引數,同時修改無需重新編譯測試程式碼。

6、JUnit 4 的測試結果通過 Green/Red bar 體現,TestNG 的結果除了 Green/Red bar,還有 Console 視窗和 test-output 資料夾,對測試結果的描述更加詳細,方便定位錯誤。

  • 詳細特性對比

下面詳細介紹一下 TestNG 與 Junit 特性對比:

1、框架整合

Spring+TestNG+Maven 整合:

  • pom.xml 中增加 testng 依賴:
<dependency>
  <groupId>org.testng</groupId>
  <artifactId>testng</artifactId>
  <version>6.8.8</version>
  <scope>test</scope>
</dependency>
  • 測試類增加 1 條註解@ContextConfiguration(locations = "classpath:applicationContext.xml")並繼承 AbstractTestNGSpringContextTests,範例如下
@ContextConfiguration(locations = "classpath:applicationContext.xml") 
public class BaseTest extends AbstractTestNGSpringContextTests{     
      @Test
      public void testMethods()     {         ......     } 
}

Spring+Junit+Maven 整合:

  • pom.xml 中增加 junit 依賴:
<!--Junit版本-->
<dependency>
  <groupId>junit</groupId>
  <artifactId>junit</artifactId>
  <version>4.4</version>
  <scope>test</scope>
</dependency>
  • 測試類增加 2 條註解

@RunWith(SpringJUnit4ClassRunner.class)

@ContextConfiguration(locations = "classpath:applicationContext.xml"),如下:

@RunWith(SpringJUnit4ClassRunner.class) 
@ContextConfiguration(locations = "classpath:applicationContext.xml") 
public class BaseTest{     
    @Test     
    public void testMethods()     {         ......     } 
}

2、註解支援

 title=

主要區別以下兩點:

1、在 JUnit 4 中,我們必須宣告“@BeforeClass”和“@AfterClass”方法作為靜態方法。TestNG 在方法宣告中更靈活,它沒有這個約束。

2、在 JUnit 4 中,註釋命名約定有點混亂,例如“Before”,“After”和“Expected”,我們並不真正瞭解“Before”和“After”之前的內容,以及要測試中的“預期” 方法。TestiNG 更容易理解,它使用類似“BeforeMethod”,“AfterMethod”和“ExpectedException”就很明瞭。

3、異常測試

“異常測試”是指從單元測試中丟擲的異常,此功能在 JUnit 4 和 TestNG 中都可實現。JUnit 4

@Test(expected = ArithmeticException.class) public void divisionWithException() {   int i = 1/0; }

TestNG

@Test(expectedExceptions = ArithmeticException.class) public void divisionWithException() {   int i = 1/0; }

4、忽略測試

忽略測試意思是在單元測試哪些是可以被忽略的,這個特性在兩個框架都已經實現。

JUnit 4

@Ignore("Not Ready to Run")  @Test public void divisionWithException() {      System.out.println("Method is not ready yet"); }

TestNG

@Test(enabled=false) public void divisionWithException() {      System.out.println("Method is not ready yet"); }

5、超時測試

時間測試意思是如果一個單元測試執行的時間超過了一個指定的毫秒數,那麼測試將終止並且標記為失敗的測試,這個特性在兩個框架都已經實現。

JUnit 4

@Test(timeout = 1000)  public void infinity() {      while(true);  }

TestNG

@Test(timeOut = 1000)  public voi

6、套件測試

“套件測試”是指捆綁幾個單元測試並一起執行。此功能在 JUnit 4 和 TestNG 中都可實現。然而,兩者都使用非常不同的方法來實現它。

JUnit 4

“@RunWith”和“@Suite”用於執行套件測試。下面的類程式碼表示在 JunitTest3 執行之後,單元測試“JunitTest1”和“JunitTest2”一起執行。所有的宣告都是在類內定義的。

@RunWith(Suite.class) @Suite.SuiteClasses({         JunitTest1.class,         JunitTest2.class }) public class JunitTest3 { }

TestNG

XML 檔案用於執行套件測試。以下 XML 檔案表示單元測試“TestNGTest1”和“TestNGTest2”將一起執行。

<suite name="My test suite">   
  <test name="testing">
    <classes>
      <class name="com.fsecure.demo.testng.TestNGTest1" />
      <class name="com.fsecure.demo.testng.TestNGTest2" />
    </classes>   
  </test>
</suite>

TestNG 可以做捆綁類測試,也可以捆綁方法測試。憑藉 TestNG 獨特的“分組”概念,每種方法都可以與一個組合相結合,可以根據功能對測試進行分類(分組)。例如,

下面是一個有四個方法的類,三個組(method1,method2 和 method3)

@Test(groups="method1") public void testingMethod1() {   System.out.println("Method - testingMethod1()"); } 
@Test(groups="method2") public void testingMethod2() {   System.out.println("Method - testingMethod2()"); } 
@Test(groups="method1") public void testingMethod1_1() { System.out.println("Method - testingMethod1_1()"); } 
@Test(groups="method4") public void testingMethod4() { System.out.println("Method - testingMethod4()"); }

使用以下 XML 檔案,可以僅使用組“method1”執行單元測試。

<suite name="My test suite">   
  <test name="testing">       
    <groups>       
      <run>         
        <include name="method1"/>       
      </run>     
    </groups>     
    <classes>        
      <class name="com.fsecure.demo.testng.TestNGTest" /></classes>   
  </test> 
</suite>

7、引數化測試

“引數化測試”是指單位測試引數值的變化。此功能在 JUnit 4 和 TestNG 中都實現。然而,兩者都使用非常不同的方法來實現它。

Junit4 引數化測試:

  • 步驟如下:

1.通過@Parameters 標識靜態引數構造方法

2.通過測試類構造方法引入引數

3.測試方法使用引數

@RunWith(value = Parameterized.class) 
public class JunitTest {       
    private int number;       
    public JunitTest6(int number) {         
        this.number = number;      
    }     
    
    @Parameters      
    public static Collection<Object[]> data() {        
        Object[][] data = new Object[][] { { 1 }, { 2 }, { 3 }, { 4 } };        
        return Arrays.asList(data);      
    }      
    
    @Test      
    public void pushTest() {        
        System.out.println("Parameterized Number is : " + number);      
    } 
}
  • 缺點:
  1. 一個測試類只能有一個靜態的引數構造方法;
  2. 測試類需要使用@RunWith(Parameterized.class),無法相容 spring-test 的 runner
  3. @RunWith(SpringJUnit4ClassRunner.class),會導致無法通過註解注入待測服務
  4. 需要在測試類中新增一個構造方法(一種冗餘設計)

TestNG 引數化測試:

  • 步驟如下:

1.通過@dataProvider 註解標識引數構造方法

2.測試方法在註解@Test 中通過 dataProvider 屬性指定引數構造方法,便可在測試方法中使用引數

@Test(dataProvider = "Data-Provider-Function")     
public void parameterIntTest(Class clzz, String[] number) {       
    System.out.println("Parameterized Number is : " + number[0]);       
    System.out.println("Parameterized Number is : " + number[1]);     
}

除此之外,TestNG 還支援通過 testng.xml 構造引數:

public class TestNGTest {  
    @Test @Parameters(value="number") 
    public void parameterIntTest(int number) {        
        System.out.println("Parameterized Number is : " + number);     
    } 
}

XML 檔案的內容如下

<suite name="My test suite">   
  <test name="testing">     
    <parameter name="number" value="2"/>     
    <classes>        
      <class name="com.fsecure.demo.testng.TestNGTest" />     
    </classes>   
  </test> 
</suite>

8、依賴測試

“引數化測試”表示方法是依賴性測試,它將在所需方法之前執行。如果依賴方法失敗,則所有後續測試將會被跳過,不會被標記為失敗。

JUnit 4

JUnit 框架著重於測試隔離; 目前它不支援此功能。

TestNG

它使用“dependOnMethods”來實現依賴測試如下

@Test public void method1() {    
    System.out.println("This is method 1"); 
}
@Test(dependsOnMethods={"method1"}) 
public void method2() {     
    System.out.println("This is method 2"); 
}

TestNG 介面自動化實踐

  • 引數化測試示例

以 DeviceStatusHSFService 為例,測試類如下:

public class DeviceStatusHSFServiceTest {

    private DeviceStatusHSFService deviceStatusHSFService;
    @BeforeTest(alwaysRun = true)
    public void beforeTest() {
        String envName = System.getProperty("maven.env");  //執行環境可配置
        SwitchENV switchEnv = new SwitchENV(envName);    //執行環境可配置
        deviceStatusHSFService = HsfRepository.getConsumer(DeviceStatusHSFService.class, switchEnv.getEnv(),
            "HSF", switchEnv.getHsfVersion(), "aicloud-device-center", switchEnv.getTargetIp()).getTarget();
    }

    @Test(dataProvider = "updateDeviceStatus", dataProviderClass = DeviceStatusHSFServiceTestDataProvider.class)
    public void updateDeviceStatusTest(Long userId, String uuid, DeviceStatus deviceStatus){
        Result<Boolean> result = deviceStatusHSFService.updateDeviceStatus(userId, uuid, deviceStatus);
        System.out.println("traceId:"+EagleEye.getTraceId()+result.toString());
        Boolean res = result.getResult();
        assertTrue(res);
    }
}

其中通過 SwitchENV 類實現執行環境可配置:

/**
 * 自定義環境配置
 */
public class SwitchENV {

    /**
     * 執行環境
     */
    private Env env;

    /**
     * hsf環境
     */
    private String hsfVersion;

    /**
     * 目標機器
     */
    private String targetIp;

    /**
     * 環境名稱
     */
    private String envName;

    public SwitchENV(String envName) {

        Properties prop = new Properties();

        // TODO: 本地自動化測試切換環境專用
        if (envName == null) {
            envName = "pre1";
        }

        switch (envName) {

            case "online": {
                InputStream in = SwitchENV.class.getClassLoader().getResourceAsStream(
                    "config/application-online.properties");
                try {
                    prop.load(in);
                } catch (IOException e) {
                    e.printStackTrace();
                }
                env = Env.ONLINE;
                break;
            }
            case "pre1": {
                InputStream in = SwitchENV.class.getClassLoader().getResourceAsStream(
                    "config/application-pre1.properties");
                try {
                    prop.load(in);
                } catch (IOException e) {
                    e.printStackTrace();
                }
                env = Env.PREPARE;
                break;
            }
            case "pre2": {
                InputStream in = SwitchENV.class.getClassLoader().getResourceAsStream(
                    "config/application-pre2.properties");
                try {
                    prop.load(in);
                } catch (IOException e) {
                    e.printStackTrace();
                }
                env = Env.PREPARE;
                break;
            }
            case "pre3": {
                InputStream in = SwitchENV.class.getClassLoader().getResourceAsStream(
                    "config/application-pre3.properties");
                try {
                    prop.load(in);
                } catch (IOException e) {
                    e.printStackTrace();
                }
                env = Env.PREPARE;
                break;
            }
            default:
                try {
                    throw new Exception("環境變數輸入錯誤!");
                } catch (Exception e) {
                    e.printStackTrace();
                }
                break;
        }
        hsfVersion = prop.getProperty("hsfVersion").trim();
        targetIp= prop.getProperty("targetIp").trim();
        this.envName = envName;
    }

    public Env getEnv() {
        return env;
    }

    public String getHsfVersion() {
        return hsfVersion;
    }

    public String getTargetIp() {
        return targetIp;
    }

    public String getEnvName() {
        return envName;
    }

}

測試引數全部放在 DeviceStatusHSFServiceTestDataProvider 類中,實現具體的請求介面、引數、校驗等資料做到與程式碼相隔離。

/**
 * 自定義環境配置
 */
public class SwitchENV {

    /**
     * 執行環境
     */
    private Env env;

    /**
     * hsf環境
     */
    private String hsfVersion;

    /**
     * 目標機器
     */
    private String targetIp;

    /**
     * 環境名稱
     */
    private String envName;

    public SwitchENV(String envName) {

        Properties prop = new Properties();

        // TODO: 本地自動化測試切換環境專用
        if (envName == null) {
            envName = "pre1";
        }

        switch (envName) {

            case "online": {
                InputStream in = SwitchENV.class.getClassLoader().getResourceAsStream(
                    "config/application-online.properties");
                try {
                    prop.load(in);
                } catch (IOException e) {
                    e.printStackTrace();
                }
                env = Env.ONLINE;
                break;
            }
            case "pre1": {
                InputStream in = SwitchENV.class.getClassLoader().getResourceAsStream(
                    "config/application-pre1.properties");
                try {
                    prop.load(in);
                } catch (IOException e) {
                    e.printStackTrace();
                }
                env = Env.PREPARE;
                break;
            }
            case "pre2": {
                InputStream in = SwitchENV.class.getClassLoader().getResourceAsStream(
                    "config/application-pre2.properties");
                try {
                    prop.load(in);
                } catch (IOException e) {
                    e.printStackTrace();
                }
                env = Env.PREPARE;
                break;
            }
            case "pre3": {
                InputStream in = SwitchENV.class.getClassLoader().getResourceAsStream(
                    "config/application-pre3.properties");
                try {
                    prop.load(in);
                } catch (IOException e) {
                    e.printStackTrace();
                }
                env = Env.PREPARE;
                break;
            }
            default:
                try {
                    throw new Exception("環境變數輸入錯誤!");
                } catch (Exception e) {
                    e.printStackTrace();
                }
                break;
        }
        hsfVersion = prop.getProperty("hsfVersion").trim();
        targetIp= prop.getProperty("targetIp").trim();
        this.envName = envName;
    }

    public Env getEnv() {
        return env;
    }

    public String getHsfVersion() {
        return hsfVersion;
    }

    public String getTargetIp() {
        return targetIp;
    }

    public String getEnvName() {
        return envName;
    }

}

思考與總結

對於介面自動化測試,從用例設計到測試指令碼實現,總結起來,需要我們具備如下思想:

  • 模組化思想
  • 資料驅動思想
  • 關鍵字驅動思想

模組化思想

對於我們的介面自動化測試工程而言,需要能夠建立小而獨立 可以描述的模組、片斷以及待測應用程式的指令碼。這些樹狀結構的小指令碼組合起來,就能組成能用於特定的測試用例的指令碼。

資料驅動思想

簡而言之,就是測試指令碼與測試資料分離。讓測試資料獨立於測試指令碼單獨存在,解除指令碼與資料之間的強耦合。測試指令碼不再負責管理測試資料,而測試資料在資料驅動測試中會以檔案或者資料庫的形式存在。指令碼每次執行會機械的從資料檔案或者資料庫中讀入測試資料,根據測試資料的不同走進不同的測試路徑。在整個測試中,測試指令碼是一成不變的,它一直機械的執行它本身的程式碼,而活著的是我們的測試資料集,我們通過不同的資料控制測試指令碼中程式碼的走向。這個思想能夠避免測試資料雜糅在測試指令碼中,方便測試資料的擴充套件。再者,在自動化測試中,為了維持迴歸測試的穩定一致,測試指令碼應當儘量避免更改。在非資料驅動的情況下,恰恰違背了這一原則。自動化測試中,隨著專案的深入,測試指令碼將會持續增多,測試資料和指令碼揉在一起?維護起來將會是一件恐怖的事情,出錯在所難免,所以這時不要這樣做,讓資料和指令碼分離,堅持死的程式碼,活的資料,維護的大部分工作將只面向資料

關鍵字驅動思想

這是一種更為高階的資料驅動測試,核心思想是將測試用例的每個步驟單獨封裝成一個函式,以這個函式名作為關鍵字,將函式名及傳參寫入檔案中,每個步驟對映一行檔案。通過解析檔案的每行內容,將內容拼成一個函式呼叫,呼叫封裝好的步驟函式,就可以一步步執行測試案例。在一個關鍵字驅動測試中,待測應用程式的功能和每個測試的執行步驟將被一起寫到一個表中。這一個思想通過很少的程式碼來產生大量的測試用例。同樣的程式碼在用資料表來產生各個測試用例的同時被複用。

當我們的測試思想越靠近上述三種型別的思想,介面測試的實現將越自動化。隨著人工智慧的不斷髮展,AI浪潮下也將誕生更多的自動化測試工具,比如採用人工智慧技術,通過某種自適應的演算法來迭代我們的測試用例,生成測試指令碼。這意味著,未來測試人員的努力方向將在設計出更加可靠、高效的自動化用例生成工具、指令碼構建工具與測試執行工具,而原先那些重複勞動的人工測試工作就讓聰明的機器幫我們做吧。

最後

PTS 介面測試免費公測中,歡迎大家適用,點選閱讀原文訪問 PTS。

更多交流,歡迎進釘釘群溝通,PTS 使用者交流釘釘群號:11774967。

此外,PTS 近期對售賣方式做了全新升級,基礎版價格直降 50%!5W 併發價格只需 199,免去自運維壓測平臺煩惱!更有新使用者 0.99 體驗版、VPC 壓測專屬版,歡迎大家點選此處選購!

 title=

相關文章