一款需要正式對外發布的產品,通常都需要經歷一個較完整的測試驗證過程,在整個產品質量驗證階段,一般會經歷幾類測試環境的驗證:從產品集成階段的測試環境->驗收階段的預發布環境->正式發布回歸的生產環境。
由於不同環境之間或多或少存在一些差異性,為了能將這些環境差異性導致的問題充分暴露出來,測試人員需要在這些不同的環境中都要進行必要的測試驗證。
接口自動化測試作為質量保障的一種手段,除了用在測試階段,也需要用在預發布環境和生產環境。
很多時候,為了能讓測試用例運行在多套環境中,不得不維護多套測試腳本、測試用例。這種方式雖然可行,但會造成大量的測試用例、測試腳本冗余,以及巨大的后期維護工作量。
那么有沒有一種方式或者說實現策略,可以實現一套接口測試用例可按照特定測試需求運行在多套環境中呢?答案是肯定的。
接下來,就帶着大家,分別從測試框架和語言實現兩個層面介紹如何實現一套測試自動化用例腳本運行在多個環境下(屬於自動化測試實施高階技巧)。

相信很多讀者能感受到一個明顯現象,公司規模越大,對各類環境的定義也會更加清晰、明確,環境種類也會進一步的細分。這么多環境的加持下,對自動化測試實施過程提出了一個挑戰或者說是需求:自動化用例應當支持在不同環境里執行,並且對用例邏輯層透明無感。
為了實現諸如此,有些人,采取了較為”傻瓜式“的方式,拿下面這段代碼為例。
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 從小工到專家》一書中的內容。
如果覺得文章對你有所幫助,動動手點贊一下以表支持,你的肯定是我創作最大的鼓勵和支持!