一套測試用例如何實現支援多個環境執行

狂師發表於2020-05-13

一款需要正式對外發布的產品,通常都需要經歷一個較完整的測試驗證過程,在整個產品質量驗證階段,一般會經歷幾類測試環境的驗證:從產品整合階段的測試環境->驗收階段的預釋出環境->正式釋出迴歸的生產環境

 

由於不同環境之間或多或少存在一些差異性,為了能將這些環境差異性導致的問題充分暴露出來,測試人員需要在這些不同的環境中都要進行必要的測試驗證。

 

介面自動化測試作為質量保障的一種手段,除了用在測試階段,也需要用在預釋出環境和生產環境。

很多時候,為了能讓測試用例執行在多套環境中,不得不維護多套測試指令碼、測試用例。這種方式雖然可行,但會造成大量的測試用例、測試指令碼冗餘,以及巨大的後期維護工作量。

 

那麼有沒有一種方式或者說實現策略,可以實現一套介面測試用例可按照特定測試需求執行在多套環境中呢?答案是肯定的

 

接下來,就帶著大家,分別從測試框架語言實現兩個層面介紹如何實現一套測試自動化用例指令碼執行在多個環境下(屬於自動化測試實施高階技巧)。

相信很多讀者能感受到一個明顯現象,公司規模越大,對各類環境的定義也會更加清晰、明確,環境種類也會進一步的細分。這麼多環境的加持下,對自動化測試實施過程提出了一個挑戰或者說是需求:自動化用例應當支援在不同環境裡執行,並且對用例邏輯層透明無感。

 

為了實現諸如此,有些人,採取了較為”傻瓜式“的方式,拿下面這段程式碼為例。

def test_login(self):
    if env = "dev":
        requests.post("https://dev.xxx.com/login", data={"username":"dev", "password":"123456"})
        do_something()
    elif env = "test":
         requests.post("https://test.xxx.com/login", data={"username":"test", "password":"123456"})
         do_something()
    elif env = "pre":
        requests.post("https://pre.xxx.com/login", data={"username":"pre", "password":"123456"})
        do_something()
    else:
        requests.post("https://www.xxx.com/login", data={"username":"superadmin", "password":"123456"})
        do_something()

  

看完上述程式碼寫法,有沒有似曾相識的同學,如果有的話,很不幸地告訴你,你採取了最不為推薦的方法。上述示例還僅僅只是一條用例,如果自動化測試用例體量大(實際都不會小),自動化測試用例指令碼維護程式碼重複量帶來的災難性可想而知。

仔細分析一下,要實現一套測試用例在多環境下執行,要解決哪些問題:

  • 不同環境的服務入口地址不同,一般還會有http/https的差別
  • 不同環境需要使用不同的測試資料
  • 一些中介軟體,比如資料庫、訊息佇列、快取服務的訪問地址、賬號、配置有差別
  • 不同環境的第三方回撥地址有差別
  • 不同環境的配置需要整體切換,不能出現在測試環境裡用了生產環境的資料的問題

 

以上都是些常見的問題,實際不同公司下,有些業務功能在實現上都還存在差異,不過這種就不在我們討論的範圍內了。

 

針對上述的這些主要問題,歸納總結一下,不難發現,在不同的環境中,對於同一個介面測試來說,測試過程的邏輯基本都是一樣的,而造成不同環境用例無法複用的因素最主要有兩個:

所呼叫到的服務域名地址不同,不同環境對應的域名是不同的。比如測試環境的域名為test.xxx.com,而正式環境的域名對應為www.xxx.com。

測試資料、配置資料不同,不同環境對應的測試資料和用到的配置資料存在不同。比如測試環境對應的使用者ID為123456,但在正式環境對應的使用者ID可能就變成了654321。

 

而針對不同環境,呼叫的服務域名地址不同,解決該問題的基本思路用兩個關鍵詞概括:抽象、列舉。

如何抽象,如何列舉,下面分別從測試框架(以Robot Frameowork框架為例)和語言實現層面(以Python語言)為大家逐一介紹。

1. 測試框架支援多環境執行思路

下述以Robot Framework框架為例,介紹如何實現一套測試用例支援多個不同執行環境,不同框架實現思路皆相通,其它框架可供參考借鑑。

在RF框架下,實現此類需求,總的原則是利用:外部變數檔案+全域性動態變數,將介面測試指令碼中涉及傳入域名的值統一封裝抽離為一個統一的公共環境變數,並且將各個不同環境域名統一存放到一個公共環境配置變數檔案中。

先來看一則指令碼片段截圖:

可以看到,在呼叫request_post關鍵字發起POST請求時,需要傳入域名地址${URL}、介面路徑${path}、介面引數${datas}等。而對於同一個介面,不管是在哪個環境下,介面路徑${path}和介面引數${datas}都應該是一樣的。而對於介面地址${URL},在不同環境中對應的值會有所不同。但從圖中我們並沒有發現${URL}變數定義的位置,它的值是從哪裡傳進來的呢?

關於介面地址${URL}變數值動態引入,通常有兩種方式。

  • 通過外部變數檔案引入。
  • 通過全域性動態引數引入。

1.1 通過外部變數檔案引入

(1)定義好介面測試實戰專案的目錄結構,在Resource | Lib 目錄下,建立環境配置變數檔案,檔名稱定義為config.py,檔案內容如下:

# coding=utf-8
# 環境配置檔案

# 測試環境
# URL = 'https://test.xxx.com'

# 預釋出環境
# URL = 'https://pre.xxx.com'

# 生產環境
URL = 'https://www.xxx.com'

 

在config.py環境配置檔案中,定義各個不同環境(測試環境、預釋出環境、生產環境)的服務域名地址,且變數名統一為URL。在執行介面測試時,保留當前需要執行測試用例的環境地址,其他環境變數註釋掉即可。

 

在實際專案當中,config.py配置檔案中的地址替換成真實的介面服務地址即可,例如,上述配置檔案中保留了生產環境的地址,此時執行介面測試用例,則呼叫的為生產環境的介面測試。需要注意的是,在同一個專案下,不同環境下的介面服務地址需要採用相同的變數名稱,定義好後,在Robot Framework測試指令碼中直接通過${URL}變數形式來引用環境變數值。

 

(2)環境配置變數檔案建立好後,選擇Resource | Business| 業務資原始檔,在資原始檔Settings配置選項中選擇Add Variables新增變數檔案,依次選擇config.py配置檔案儲存路徑,如圖所示。

(3)config.py變數檔案匯入成功後,當需要在不同環境下執行介面測試用例時,可在用例指令碼不做任何變更的情況下,只需要更改config.py配置檔案中的地址即可實現一鍵切換介面測試執行環境。

 

1.2 全域性動態引數引入

通過外部變數檔案的形式引入,雖然可以實現在測試指令碼不做任何變更的前提下完成一套用例多套環境執行的目的,但每次在不同環境執行時,需要去環境變數檔案中進行調整,雖然調整幅度較小(只需要進行註釋),但仍然不是那麼便捷。

在Robot Framework中還在一種更便捷靈活的方式來實現此目的,即通過全域性引數變數引用形式來實現對應變數值的全域性動態修改。而採用引數變數引用的形式來實現變數值的動態修改,也分為兩種方式。

1) 第一種方式:Arguments引數欄

在RIDE編輯器Run執行標籤下的Arguments引數欄中增加引數變數--variable key:value。如下圖所示,增加了一個變數名為URL,變數值為https://test.xxx.com。

引數欄中增加變數的書寫格式:

-v變數名:變數值或者--variable變數名:變數值。

上圖中,引數欄填入-v URL:https://test.xxx.com,對URL變數賦值為https://test.xxx.com。這樣在執行介面測試用例時,會將URL對應的變數值動態修改賦值為https://test.xxx.com。此時即使環境變數檔案中的URL變數為https://www.xxx.com。通過這種命令列引數變數的引入形式仍然可以實現動態修改URL值。

 

PS: 通過引數變數--variable key:value形式引入的變數值,為全域性變數優先順序最高。

 

2) 第二種方式:命令列引數

採用Pybot或Robot命令列的形式來執行Robot Framework介面測試用例時,引入引數變數替換,例如:

Robot --variable URL:"https://test.xxx.com" /usr/local/rf_api || exit 0

 

此種方式也是最為常用的呼叫形式,適合與CI持續整合系統對接。

 

2. 語言層面支援多環境執行思路

以Python語言為例,從語言層面解決如何一套用例支援多環境執行,本質還是要在用例層對測試環境無感,需要把環境所用的資料抽象出來。

隨便畫了一張草圖,大家湊合著看,圖中所示,是一個典型的橋接模式(Bridge Pattern):將抽象部分與實現部分分離,使它們都可以獨立地變化。

拿上述最開始的程式碼示例來講:需要抽象出服務地址賬號兩個物件,用例邏輯層只允許使用這些抽象的物件,而不能直接訪問具體的資料,例如改成如下:

def test_login(self):
    requests.post(entrypoint.URL+"/login", data={"username":data.account.username, "password":data.account.password})

 

而在Python中,可以利用property裝飾器來實現簡易的橋接模式(其它語言也有類似的設計模式或實現),程式碼示例如下:

from enum import Enum

class Environment(Enum):
    DEV = 0
    TEST = 1
    PRE = 2
    PROD= 3

class EntryPoint:
    _ENV_URL = {
        Environment.DEV: "https://dev.xxx.com",
        Environment.TEST: "https://test.xxx.com",
        Environment.PRE: "https://pre.xxx.com",
        Environment.PROD: "https://www.xxx.com"
    }

    @property
    def URL(self):
        return self._ENV_URL[env]

env = Environment.DEV   # 作為全域性的環境變數

 

樣例程式碼中,先通過繼承Enum類實現了一個列舉類Environment,在列舉類中定義了各環境的常量,可以理解是為後續定義各環境具體的Key值。

 

接著定義了一個EntryPoint類,並且在該類中,定義了一個儲存各環境的字典,KEY名為列舉類中定義的常量。通過在URL方法 ,增加@property裝飾器,可以讓URL方法變成只讀屬性,並且通過obj.URL即可呼叫。

 

如果需要切換環境去執行,只要更新全域性變數env就可以實現。

如果你對Python中的,Enum列舉用法和@property裝飾器,還不瞭解或者想更深入瞭解這些用法,具體可參考官方文件介紹。

# @property用法官方文件
https://docs.python.org/3/library/functions.html#property
# Enum用法官方文件
https://docs.python.org/zh-cn/3/library/enum.html

 

受篇幅限制,一套自動化測試用例,不同環境對應的測試資料不同,解決思路下回再介紹,完整的自動化測試設計規範及各類實戰技巧,建議可以系統性地學習:《自動化測試實戰寶典:Robot Framework + Python 從小工到專家》一書中的內容。

 

如果覺得文章對你有所幫助,動動手點贊一下以表支援,你的肯定是我創作最大的鼓勵和支援!

 

 
 

相關文章