HttpRunner 通過 skip 機制實現對測試用例的分組執行控制
背景介紹
近期,某位同學對HttpRunner
提了一個需求點:
能否支持類似unittest中的skip註解,方便靈活剔除某些用例,不執行。
目前在介面測試日常構建中,會遇到一些介面開發暫時屏蔽了或者降級,導致用例執行失敗;所以想當遇到這些情況的時候,能夠臨時剔除掉某些用例不執行;等後續恢復後,再去掉,然後恢復執行。
針對這種情況,HttpRunner
的確沒有直接支持。之所以說是沒有直接
支持,是因為在HttpRunner
中存在times
關鍵字,可以指定某個test
的運行次數。
例如,如下test
中指定了times
為3,那麼該test
就會運行3次。
- test: name: demo times: 3 request: {...} validate: [...]
假如要實現臨時屏蔽掉某些test
,那麼就可以將對應test
的times
設置為0。
這雖然也能勉強實現需求,但是這跟直接將臨時不運行的test
注釋掉沒什麼區別,都需要對測試用例內容進行改動,使用上很是不方便。
考慮到該需求的普遍性,HttpRunner
的確應該增加對該種情況的支持。
在這方面,unittest
已經有了清晰的定義,有三種常用的裝飾器可以控制單元測試用例是否被執行:
- a href="https://testerhome.com/unittest.skip">@unittest.skip(reason):無條件跳過當前測試用例
- a href="https://testerhome.com/unittest.skipIf">@unittest.skipIf(condition, reason):當條件表達式的值為true時跳過當前測試用例
- a href="https://testerhome.com/unittest.skipUnless">@unittest.skipUnless(condition, reason):當條件表達式的值為false時跳過當前測試用例
該功能完全滿足我們的需求,因此,我們可以直接復用其概念,嘗試實現同樣的功能。
實現方式
目標明確了,那需要怎麼實現呢?
首先,我們先看下unittest
中這三個函數是怎麼實現的;這三個函數定義在unittest/case.py
中。
class SkipTest(Exception): """ Raise this exception in a test to skip it. Usually you can use TestCase.skipTest() or one of the skipping decorators instead of raising this directly. """ passdef skip(reason): """ Unconditionally skip a test. """ def decorator(test_item): if not isinstance(test_item, (type, types.ClassType)): @functools.wraps(test_item) def skip_wrapper(*args, **kwargs): raise SkipTest(reason) test_item = skip_wrapper test_item.__unittest_skip__ = True test_item.__unittest_skip_why__ = reason return test_item return decoratordef skipIf(condition, reason): """ Skip a test if the condition is true. """ if condition: return skip(reason) return _iddef skipUnless(condition, reason): """ Skip a test unless the condition is true. """ if not condition: return skip(reason) return _id
不難看出,核心有兩點:
- 對於
skip
,只需要在該測試用例中raise SkipTest(reason)
,而SkipTest
是unittest/case.py
中定義的一個異常類; - 對於
skipIf
和skipUnless
,相比於skip
,主要是需要指定一個條件表達式(condition),然後根據該表達式的實際值來決定是否skip
當前測試用例。
明確了這兩點之後,我們要如何在HttpRunner
中實現同樣的功能,思路應該就比較清晰了。
因為HttpRunner
同樣也是採用unittest
來組織和驅動測試用例執行的,而具體的執行控制部分都是在httprunner/runner.py
的_run_test
方法中;同時,在_run_test
方法中會傳入testcase_dict
,也就是具體測試用例的全部信息。
那麼,最簡單的做法,就是在YAML/JSON
測試用例中,新增skip/skipIf/skipUnless
參數,然後在_run_test
方法中根據參數內容來決定是否執行raise SkipTest(reason)
。
例如,在YAML
測試用例中,我們可以按照如下形式新增skip
欄位,其中對應的值部分就是我們需要的reason
。
- test: name: demo skip: "skip this test unconditionally" request: {...} validate: [...]
接下來在_run_test
方法,要處理就十分簡單,只需要判斷testcase_dict
中是否包含skip
欄位,假如包含,則執行raise SkipTest(reason)
即可。
def _run_test(self, testcase_dict): ... if "skip" in testcase_dict: skip_reason = testcase_dict["skip"] raise SkipTest(skip_reason) ...
這對於skip
機制來做,完全滿足需求;但對於skipIf/skipUnless
,可能就會麻煩些,因為我們的用例是在YAML/JSON
文本格式的文件中,沒法像在unittest
中執行condition
那樣的Python表達式。
嗯?誰說在YAML/JSON
中就不能執行函數表達式的?在HttpRunner
中,我們已經實現了該功能,即:
- 在
debugtalk.py
中定義函數,例如func(a, b)
- 在
YAML/JSON
中通過${func(a,b)}
對函數進行調用
在此基礎上,我們要實現skipIf/skipUnless
就很簡單了;很自然地,我們可以想到採用如下形式來進行描述。
- test: name: create user which existed (skip if condition) skipIf: ${skip_test_in_production_env()} request: {...} validate: [...]
其中,skip_test_in_production_env
定義在debugtalk.py
文件中。
def skip_test_in_production_env(): """ skip this test in production environment """ return os.environ["TEST_ENV"] == "PRODUCTION"
然後,在_run_test
方法中,我們只需要判斷testcase_dict
中是否包含skipIf
欄位,假如包含,則將其對應的函數表達式取出,運行得到其結果,最後再根據運算結果來判斷是否執行raise SkipTest(reason)
。對函數表達式進行解析的方法在httprunner/context.py
的exec_content_functions
函數中,具體實現方式可閱讀之前的文章。
def _run_test(self, testcase_dict): ... if "skip" in testcase_dict: skip_reason = testcase_dict["skip"] raise SkipTest(skip_reason) elif "skipIf" in testcase_dict: skip_if_condition = testcase_dict["skipIf"] if self.context.exec_content_functions(skip_if_condition): skip_reason = "{} evaluate to True".format(skip_if_condition) raise SkipTest(skip_reason) ...
skipUnless
與skipIf
類似,不再重複。
通過該種方式,我們就可以實現在不對測試用例文件做任何修改的情況下,通過外部方式(例如設定環境變數的值)就可以控制是否執行某些測試用例。
效果展示
skip/skipIf/skipUnless
機制實現後,我們對測試用例的執行控制就更加靈活方便了。
例如,我們可以很容易地實現如下常見的測試場景:
- 對測試用例進行分組,P0/P1/P2等,然後根據實際需求選擇執行哪些用例
- 通過環境變數來控制是否執行某些用例
更重要的是,我們無需對測試用例文件進行任何修改。
在HttpRunner
項目中存在一個示例文件,httprunner/tests/data/demo_testset_cli.yml
,大家可以此作為參考。
在運行該測試集後,生成的測試報告如下所示。
最後做個預告,HttpRunner
在中文使用文檔方面一直比較缺失,近期將集中時間進行梳理,爭取儘快給大家一個比較系統的文檔手冊。
推薦閱讀:
※【Python3網路爬蟲開發實戰】 1.3.2-Beautiful Soup的安裝
※利用內存破壞實現python沙盒逃逸
※從零開始寫Python爬蟲 --- 2.3 爬蟲實踐:天氣預報&數據存儲
※Python-NumPy模塊的轉置軸對換(2)