概要
本文主要介紹了如何在測試資源(被測對象)受限的情況下,使用 pytest 進行并發測試以減少總體測試時間的方法和過程。
背景
在軟件開發過程中,我們通常使用測試用例來持續保證軟件的質量(例如,確保關鍵功能不被破壞,確保相關流程不會阻塞等)。從 CI/CD 的概念出發,要做到持續地保證軟件質量,我們需要不間斷地跑相關測試用例(例如,merge PR 之前、每日測試等),以保證軟件持續處于高質量水平。
在這個過程中,隨著總體代碼量和功能的增加,我們的用例集也會不斷膨脹。然而,當我們的測試集數量達到一個較大的數量時,跑一次完整的測試用例集會到達一個很恐怖的時間(例如,筆者所在的項目當前跑一遍完整測試用例的時間需要超過 3 天)。此時,將其嵌入流水線過程將變成一個不可接受的事情,利用其持續保證版本質量更是無從說起。
因此,在總體測試時間超過一個區間后,我們應當自然而然地會產生優化執行時間的想法,例如優化用例,減少大用例的執行時間等。我們這里主要介紹的是使用并發的方法減少總耗時——一般來說,這也是最立竿見影的方法。
在一般的軟件測試項目中,被測應用程序(AUT, Application Under Test)一般不是測試的并發瓶頸。
例如,在 web 項目中,測試過程一般如下:
- 起一個 AUT(包括該 web 項目相關的數據庫、后端等)
- 通過 http request、browser driver 等方式,訪問 web 服務對應的端口并進行測試
- 所有測試完成后,關閉 AUT
注意到,在這個過程中,AUT 本身是支持并發的(畢竟你可不想你的網站只有一個用戶可以訪問),且其運行環境一般支持較高的并發訪問需求。因此其進行整體測試的時間開銷主要體現在固有開銷上(例如,網絡往返時間、io 時間、等待同步等),而不是受限于 AUT。
因此,如果你的開發場景如上,你可以肆無忌憚地并發進行測試——在搜索引擎上搜索 pytest-xdist
一般能讓你很輕易地解決你的問題。然而,對于一些不幸的人,測試資源會比較受限,因此無法簡單地使用通用方法解決問題。比如筆者就需要在這個場景下尋找一個解決方案。
筆者正在為一個測試框架做開發工作,其相關測試用例集主要遵循以下規則進行測試:
- 通過 netconf、ssh 或 http 等協議連接到待測設備(DUT, device under test)
- 通過不同協議的連接,對設備進行一系列表更并進行驗證
- 測試完成后,繼續測試下一個用例
在此場景中,DUT 是一臺性能極其低下的網絡設備(可以理解為路由器),修改其相關配置會限制影響流量的表現。因此,無論是從性能還是從功能上,該設備都不支持并發測試。
怎么辦?加機器!既然一臺機器無法支撐并發測試,那再加幾臺機器,將測試用例散列到各設備上不就行了?下面我們將進行進一步探索,總結出一個具體可行的方案。
解決方案
為了充分利用 pytest 生態,我們使用 pytest-xdist
實現并發功能,其相比于 pytest-parallel
對于 pytest-html
和 allure-pytest
等周邊生態的兼容性更好。已知 pytest-xdist 使用了多進程實現的并發操作,因此我們接下來的一切并發控制都基于多進程進行考慮(當然實際上也很容易切換到多線程生態)。
而實現在受限資源下進行測試的方法也很簡單:我們只需要列出所有可用于測試的設備,然后在測試的時候自動選中基于哪臺設備進行測試就好了。
其具體做法如下:
- 首先,我們將所有設備的信息寫入到一個 json 文件中(或者數據庫等任何你想要的地方都行)
- 然后,以并發的方式執行測試
- 以
pytest.fixure
的方式為每個測試用例設置夾具,使其在運行前選中并鎖定用例需要的設備 - 在選定的設備上執行用例內容
- 執行結束,釋放設備鎖以備下一個用例使用
當然,上面說的只是最理想的情況,實際工程應用中,還應考慮到更多問題:
- 如果存在不同型號的設備,有些設備可能缺失某種特性導致無法執行某些測試用例,應該如何正確選擇設備?
- 如果一個測試用例需要同時連接到不同的機器上做驗證(如交叉校驗,雙機備份等場景),應如何處理?
- 如果多個用例使用同一批資源,是否可能產生死鎖問題?
這些問題需要更加深入地理解當前項目的用例執行方式,并不能不加區分放到一起討論,故我們在此不繼續討論。
資源
綜上過程,筆者創建了一個簡單的實現示例:focksor/notebook_demo__pytest_limited_concurrency。希望對你有幫助