分析:如何多線程運行測試用例


這是時常被問到的問題,尤其是UI自動化的運行,過程非常耗時,所以,所以多線程不失為一種首先想到的解決方案。

多線程是針對的測試用例,所以和selenium沒有直接關系,我們要關心的是單元測試框架。

unittest

首先,應該說明的是unittest本身是不支持多線程的。當然,如果你學過Python的threading模塊,也未必不行。不過我在stackoverflow 找了半天,大多是介紹unittest 測試多線程模塊,並非是unittest本身如何多線程運行用例。

“我如何學習葵花寶典” 和 “我如何驗證 張三 學會了葵花寶典”是兩回事,而我顯然要解決的問題是前者。

又重新百度,結果就找了答案。核心借助 tomorrow3 的測試庫,再配合HTMLTestRunner 生成測試報告。

https://github.com/SeldomQA/HTMLTestRunner
https://github.com/dflupu/tomorrow3

  • 測試用例

測試用例如下,你可以復制多份測試。

# test_a.py
import time
import unittest
from selenium import webdriver


class Test1(unittest.TestCase):

    @classmethod
    def setUpClass(cls):
        cls.driver = webdriver.Chrome()

    def test_01(self):
        self.driver.get("https://www.bing.com/?mkt=zh-CN")
        elem = self.driver.find_element_by_id("sb_form_q")
        elem.send_keys("多線程")
        elem.submit()
        time.sleep(2)
        self.assertIn("多線程", self.driver.title)

    @classmethod
    def tearDownClass(cls):
        cls.driver.quit()


if __name__ == "__main__":
    unittest.main()
  • 運行文件

核心在tomorrow3提供的threads裝飾器,用於裝飾測試運行方法。

import unittest
import os
from TestRunner import HTMLTestRunner
from tomorrow3 import threads

# 定義目錄
BASE_DIR = os.path.dirname(os.path.realpath(__file__))
TEST_DIR = os.path.join(BASE_DIR, "test_dir")
REPORT_DIR = os.path.join(BASE_DIR, "test_report")


def test_suits():
    """
    加載所有的測試用例
    """
    discover = unittest.defaultTestLoader.discover(
        TEST_DIR,
        pattern="test_*.py"
    )
    return discover


@threads(2)  # !!!核心!!!! 設置線程數
def run_case(all_case, nth=0):
    """
    執行所有的用例, 並把結果寫入測試報告
    """
    report_abspath = os.path.join(REPORT_DIR, f"result{nth}.html")
    with open(report_abspath, "wb+") as file:
        runner = HTMLTestRunner(stream=file, title='多線程測試報告')
        # 調用test_suits函數返回值
        runner.run(all_case)


if __name__ == "__main__":
    cases = test_suits()
    # 循環啟動線程
    for i, j in zip(cases, range(len(list(cases)))):
        run_case(i, nth=j)  # 執行用例,生成報告

總結:

確實可以(根據設置的線程數)同時開啟兩個瀏覽器運行,同時帶來了兩個問題。

  1. 程序會根據線程數,生成多份測試報告,每個測試報告統計當前線程所運行的測試結果。
  2. 如果去掉nth參數,設置為一個報告,那么第二個線程運行的結果不能正確展示的一張報告上。
  3. 至少每個線程中的用例要保證有瀏覽器的啟動/關閉。

第3點,你可能不理解,我舉個例子,小時候見過媽媽/奶奶縫衣服。假設一件衣服拿一根針來縫制,縫衣服最麻煩就是穿針引線,針眼很小,每次都要費半天功夫,小孩子眼神好,我時常被叫去穿針引線。那為了節約時間怎么辦,我當然是把線弄的長長的,最好是一根針線可以縫制多件衣服。

在Selenium 中,定義的瀏覽器驅動driver,每一條用例都定義一次驅動,伴隨而來的就每個用例都開啟/關閉一次瀏覽器,這當然是非常耗時的,為了縮短這個時間,我們最好是啟動一次瀏覽器把所有用例都跑完才關閉。

那么問題來了,當我們使用多線程之后,相當於多個人同時縫制衣服,共用一根針線肯定不行啊。至少需要每人一根針線吧!而且縫制衣服的人是變化的,為了趕時間就多兩個人(多開兩個線程),用例比較少就少幾個人(少啟兩個線程),為了適應這種變化,那么最好一件衣配置一根針線,每件衣服是最小單位,一個人必須要獨立的把一件衣服縫好。

為了使用多線程,我們就必須每條用例開啟/關閉一次瀏覽器,這其實是另一種時間的浪費,為了彌補這里的浪費,你必須把線程開得足夠多才行。

最后,你必須再配置一位統計員,去統計每個人縫制衣服的數量,合計到一個報告中。

pytest

多線程運行用例在 pytest 中就相對容易太多了,pytest-xdist 插件就可以完成這件事情。

https://github.com/pytest-dev/pytest-xdist

  • 測試用例

測試用例如下,你可以復制多份測試。

# test_a.py
import time
from selenium import webdriver


def test_01():
    driver = webdriver.Chrome()
    driver.get("http://www.baidu.com")
    elem = driver.find_element_by_id("kw")
    elem.send_keys("unittest")
    elem.submit()
    time.sleep(2)
    assert driver.title == "unittest_百度搜索"
    driver.quit()


def test_02():
    driver = webdriver.Chrome()
    driver.get("http://www.baidu.com")
    elem = driver.find_element_by_id("kw")
    elem.send_keys("python")
    elem.submit()
    time.sleep(2)
    assert driver.title == "python_百度搜索"
    driver.quit()

  • 運行文件

核心就是安裝 pytest-xdist 插件,通過-n參數設置線程數即可。

import os
import pytest

# 定義目錄
BASE_DIR = os.path.dirname(os.path.realpath(__file__))
TEST_DIR = os.path.join(BASE_DIR, "test_dir")
REPORT_DIR = os.path.join(BASE_DIR, "test_report")


if __name__ == '__main__':
    report_file = os.path.join(REPORT_DIR, "result.html")
    pytest.main([
        "-n", "2",  # !!核心!!!設置2個線程
        "-v", "-s", TEST_DIR,
        "--html=" + report_file,
        ])

總結:

相對於unittest,在pytest實現多線程非常簡單,最終線線程的測試結果也可以很好在一個測試報告中展示。

  1. 存在的問題,正如我上面討論的,你必須為每個用例設置開啟/關閉。除了額外消耗啟動時間外,如果想統一配置用例通過哪個瀏覽器執行也會比較麻煩。
  2. 你的每一條用例應該保持絕對獨立,不能出現這條用例依賴上條用例的執行結果。因為,這兩條用例可能會分配由不同的線程執行。
  3. 正如上一條的原因,你不能控制用例的執行順序。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM