一、Unittest 框架概述

1.1 起源與發展

  • Unittest是Python標準庫中內置的單元測試框架,它的設計理念深受Java領域著名測試框架JUnit的啓發。作為一個免費開源的測試解決方案,Unittest在Python生態系統中扮演着至關重要的角色。
  • 該框架在設計層面完全基於面向對象的設計思維,這意味着要熟練使用Unittest,開發者需要具備一定的面向對象編程基礎。
  • 這種面向對象的設計使得測試代碼更加結構化、可維護性更強,同時也為框架的擴展提供了良好的基礎。

1.2 核心職責

Unittest框架主要負責三個核心功能:

  • 創建和管理測試用例:提供TestCase基類來定義和組織測試方法
  • 組織和調用測試套件:通過TestSuite來批量管理和執行測試用例
  • 生成測試報告:提供多種格式的測試結果報告,方便結果分析

1.3 Unittest框架特性

  • 支持測試自動化
  • 配置共享和代碼關聯測試
  • 能將測試樣例聚合到測試集中
  • 實現測試執行與報告框架的分離。
  • 這種分離設計使得開發者可以根據項目需求自定義測試報告格式,提高了框架的靈活性。

1.4 核心組件

  • 測試用例(TestCase)、測試夾具(TestFixture)、測試套件(TestSuite)
  • 測試執行器(TestRunner)、測試報告(TestReport)、加載測試用例的加載器(TestLoader)

1.5 Unittest測試框架的核心價值

  • 幫助測試人員更容易、快速地定義和維護測試用例;
  • 支持測試用例的複用,使代碼維護性和擴展能力得到提升;
  • 提供豐富的斷言方法,支持測試腳本的執行;
  • 對數據驅動測試(DDT)和YAML格式的數據有着完美支持;
  • 嘗試執行與測試報告分離,使開發者可以自由擴展自己的測試報告;
  • 將測試用例、套件和執行器等程序腳本納入版本管理體系,為後續迭代改進和成果積累提供支持。

1.6 在Python測試生態中的位置

  • 在Python的測試工具生態中,Unittest扮演着基礎框架的角色。
  • Unittest有着標準庫的地位、穩定的API和廣泛的項目應用,
  • 是許多企業和開源項目的首選測試解決方案。

二、測試框架架構:六大核心組件深度解析

2.1 測試用例(TestCase)—— 測試的基本單元

  • 測試用例是Unittest框架的基礎構建塊,每個測試用例都是unittest.TestCase類的子類。
  • 面向對象的設計使得測試代碼具有良好的封裝性複用性

測試用例示例:

import unittest

class MyTestCase(unittest.TestCase):
    def test_1(self):
        #模擬測試的相關操作
        print("do test 1")
#unittest 中測試用例是一個類,需要調用其中的用例級執行方法main()來運行
if __name__ == '__main__':
    unittest.main()

測試方法的執行順序:

Unittest 中測試方法的執行順序並非按代碼編寫順序,而是根據方法名的 ASCII 碼升序排列(即按字母 / 數字順序排序)。這一規則確保了用例執行的確定性,避免因代碼順序調整導致執行邏輯混亂:

  • 有如下亂序測試函數:
import unittest

class MyTestCase(unittest.TestCase):
    def test_3(self):
        # 模擬測試的相關操作
        print("do test 3")
    def test_1(self):
        #模擬測試的相關操作
        print("do test 1")
    def test_2(self):
        #模擬測試的相關操作
        print("do test 2")
    def demo(self):
        print("demo")   #非test_開頭,不會被執行
#unittest 中測試用例是一個類,需要調用其中的用例級執行方法main()來運行
if __name__ == '__main__':
    unittest.main()
  • 執行結果如下

異常捕獲與報告:

  • Unittest測試框架的另一個功能就是可以自行處理測試方法中拋出的異常,無需開發者或測試者自行編寫try…except…語句。

在Unittest測試框架中編寫測試用例的注意點:

  • Unittest是Python標準庫,無需安裝,直接使用即可;
  • 測試用例必須繼承TestCase父類;
  • 測試方法必須以test_開頭;
  • 測試方法的命名排序決定了測試方法的執行順序,測試用例類同理;
  • 一般方法可以被測試方法調用和執行;
  • 編寫測試用例時不需要主動捕獲異常信息,除非測試邏輯需要。

2.2 測試夾具(TestFixture)—— 測試的前置與後置處理

  • 測試夾具(TestFixture)是Unittest中用於管理測試環境的核心機制,它通過前置操作(如初始化資源)和後置操作(如清理資源),確保每個測試用例在獨立、一致的環境中執行。
  • 常見的資源包括數據庫連接、文件句柄、網絡會話等,避免用例之間因資源共享導致的依賴問題。

2.2.1 方法級測試夾具

方法級夾具作用於每個測試方法,即每個test_開頭的方法執行前後,都會觸發對應的前置和後置操作。核心方法為:

  • setUp():測試方法執行前自動調用,用於初始化資源(如記錄開始時間戳、連接數據庫)。
  • tearDown():測試方法執行後自動調用,用於清理資源(如結束時間戳、斷開數據庫連接)。
  • 示例代碼:
import unittest

class MyTestCase(unittest.TestCase):
    def setUp(self):
        print("----setUp----")
        #打開測試的地址
        #記錄開始時間

    def tearDown(self):
       print("----tearDown----")
       #關閉網頁
       #終止計算時間

if __name__ == '__main__':
    unittest.main()
  • 執行結果:
----setUp----
do test 1
----tearDown----
-----------------------------------------------------
進程已結束,退出代碼為 0

2.2.2 用例級測試夾具

用例級夾具作用於整個測試類,即測試類中所有方法執行前執行一次前置操作,所有方法執行後執行一次後置操作。核心方法為:

  • setUpClass():測試類中所有方法執行前調用(需用@classmethod裝飾),用於初始化全局資源(如啓動服務器)。
  • tearDownClass():測試類中所有方法執行後調用(需用@classmethod裝飾),用於銷燬全局資源(如關閉服務器)。
  • 示例代碼:
import unittest

    @classmethod
    def setUpClass(cls) -> None:
       print("----setUpClass----")
       #獲取連接數據庫
       #獲取Selenium驅動

    @classmethod
    def tearDownClass(cls) -> None:
        print("----tearDownClass----")
        #關閉連接數據庫
        #退出Selenium驅動

if __name__ == '__main__':
    unittest.main()
  • 執行結果:
----setUpClass----
----setUp----
do test 1
----tearDown----
----setUp----
do test 2
----tearDown----
----tearDownClass----
----------------------------------------------------------------------
進程已結束,退出代碼為 0

2.2.3 測試夾具的參數傳遞

測試夾具中初始化的資源(如setUp()中定義的self.file_path)可通過實例變量傳遞給測試方法,無需在每個方法中重複定義。例如在方法級夾具中,setUp()中定義的self.xxx可直接在test_xxx方法中使用。

  • 示例代碼(參數傳遞):
import unittest

class TestCalculator(unittest.TestCase):
    def setUp(self):
        # 初始化測試數據(通過實例變量傳遞)
        self.a = 10
        self.b = 5

    def test_add(self):
        # 使用setUp中定義的參數
        result = self.a + self.b
        self.assertEqual(result, 15, "加法計算錯誤")

    def test_subtract(self):
        # 使用setUp中定義的參數
        result = self.a - self.b
        self.assertEqual(result, 5, "減法計算錯誤")

if __name__ == '__main__':
    unittest.main()

2.2.4 測試夾具使用總結

  • 方法級vs用例級:方法級夾具(setUp/tearDown)適合為每個用例獨立初始化資源(如臨時文件),用例級夾具(setUpClass/tearDownClass)適合全局資源(如服務器連接)。
  • 執行時機setUpClass → 所有setUp → 測試方法 → 所有tearDowntearDownClass
  • 異常影響:若setUp()執行失敗,對應的測試方法會被跳過;若setUpClass()失敗,整個測試類的方法都會被跳過。
  • 複用性:可通過繼承測試類複用夾具邏輯(如創建BaseTest類封裝通用夾具,其他測試類繼承它)。

2.3 測試用例斷言——驗證預期結果

斷言是測試用例的核心邏輯,用於判斷實際結果與預期結果是否一致。Unittest提供了豐富的斷言方法,覆蓋常見的比較場景,若斷言失敗會拋出AssertionError,並標記用例為失敗(F)。

  • 常用斷言方法及示例:

斷言方法

作用

示例

assertEqual(a, b)

驗證a == b

self.assertEqual(1+1, 2)

assertNotEqual(a, b)

驗證a != b

self.assertNotEqual(2*3, 5)

assertTrue(x)

驗證x為True

self.assertTrue(10 > 5)

assertFalse(x)

驗證x為False

self.assertFalse(0 == 1)

assertIs(a, b)

驗證a is b(內存地址相同)

self.assertIs(None, None)

assertIsNone(x)

驗證x is None

self.assertIsNone(None)

assertIn(a, b)

驗證a在b中(b為可迭代對象)

self.assertIn("a", ["a", "b"])

assertNotIn(a, b)

驗證a不在b中

self.assertNotIn("c", ["a", "b"])

assertRaises(exc, func, *args)

驗證調用func(*args)時拋出exc異常

self.assertRaises(ValueError, int, "abc")

示例代碼(綜合使用斷言):

import unittest

class TestAssertions(unittest.TestCase):
    def test_math_operations(self):
        self.assertEqual(2 + 3, 5)
        self.assertNotEqual(10 / 2, 6)
        self.assertTrue(5 > 3)
        self.assertFalse(0 == 1)

    def test_data_structures(self):
        fruits = ["apple", "banana"]
        self.assertIn("apple", fruits)
        self.assertNotIn("orange", fruits)
        self.assertIsNone(None)

    def test_exception_handling(self):
        # 驗證轉換"abc"為整數時拋出ValueError
        self.assertRaises(ValueError, int, "abc")

if __name__ == '__main__':
    unittest.main()

2.4 方法跳過——選擇性執行測試用例

在實際測試中,有時需要跳過某些用例(如環境不支持、功能未實現),Unittest提供了裝飾器實現跳過邏輯,常用的有:

  1. @unittest.skip(reason):無條件跳過用例,reason為跳過原因(必填)。
  2. @unittest.skipIf(condition, reason):當condition為True時跳過。
  3. @unittest.skipUnless(condition, reason):當condition為False時跳過。
  4. @unittest.expectedFailure:標記用例為“預期失敗”,若用例失敗則不計入錯誤統計。

示例代碼:

import unittest

class TestSkip(unittest.TestCase):
    @unittest.skip
    def test_1(self):
        print("do test 1")

    @unittest.skipIf(True, "條件成立,該方法被跳過")
    def test_2(self):
        print("do test 2")

    @unittest.skipUnless(False, "條件不成立,該方法被跳過")
    def test_3(self):
        print("do test 3")

    @unittest.expectedFailure
    def test_expected_failure(self):
        # 預期會失敗的用例(實際執行時失敗不計入錯誤)
        self.assertEqual(1, 2)

if __name__ == '__main__':
    unittest.main()

執行結果:

======================== 3 skipped, 1 xfailed in 0.39s ========================
SKIPPED                                      [ 25%]
Skipped
SKIPPED (條件成立,該方法被跳過)             [ 50%]
Skipped: 條件成立,該方法被跳過
SKIPPED (條件不成立,該方法被跳過)           [ 75%]
Skipped: 條件不成立,該方法被跳過
XFAIL                         [100%]
----------------------------------------------------------------------
進程已結束,退出代碼為 0

2.5 測試套件(TestSuite)—— 用例的批量組織與管理

試想一下,有兩個測試用例,分別為MyTestCase1和MyTestCase2,每個用例都有同名的四個測試方法,test_1()、test_2()、test_3()、test_4(),需求是執行MyTestCase1中的前兩個和MyTestCase2中的後兩個,該如何做?

  • 測試套件(TestSuite) 就出現了,它能夠一次執行指定的測試方法或者將多個測試用例中的指定方法一起執行。
  • 測試套件(TestSuite) 能高效管理多個測試用例,在不同的測試需求場景下,選擇不同範圍的測試用例,方便測試人員批量執行。
    有如下兩個測試用例:
  • MyTestCase1:
import unittest

class TestCase1(unittest.TestCase):
    def test_1(self):
        print("This is TestCase1 test_1")
    def test_2(self):
        print("This is TestCase1 test_2")
    def test_3(self):
        print("This is TestCase1 test_3")
    def test_4(self):
        print("This is TestCase1 test_4")
if __name__ == "__main__":
    unittest.main()
  • MyTestCase2:
import unittest

class TestCase2(unittest.TestCase):
    def test_1(self):
        print("This is TestCase2 test_1")
    def test_2(self):
        print("This is TestCase2 test_2") 
    def test_3(self):
        print("This is TestCase2 test_3")
    def test_4(self):
        print("This is TestCase2 test_4")
if __name__ == "__main__":
    unittest.main()

構造測試套件有4種方式:

2.5.1 方法級裝載器

  • 第一種,使用 addTest() 方法,可以將測試方法逐個添加至測試套件中
  • 每次添加一個測試方法,只適用於要構建的測試套件中的測試方法較少的情況
  • 創建測試腳本:
import unittest
from MyTestCase1 import TestCase1
from MyTestCase2 import TestCase2
#實例化一個測試套件
suite = unittest.TestSuite()
#第一種裝載器:添加單個測試方法

#添加測試方法到測試套件中
suite.addTest(TestCase1('test_1'))
suite.addTest(TestCase1('test_2'))
suite.addTest(TestCase2('test_3'))
suite.addTest(TestCase2('test_4'))

#實例化執行器,執行測試套件需要使用測試執行器
runner = unittest.TextTestRunner()
#執行測試套件
runner.run(suite)
  • 執行結果如下:
PS E:\pytest_test_selenium> python -u "e:\pytest_test_selenium\TestSuite_runner.py"     
This is TestCase1 test_1
.This is TestCase1 test_2
.This is TestCase2 test_3
.This is TestCase2 test_4
.
----------------------------------------------------------------------
Ran 4 tests in 0.001s

OK

2.5.2 方法級批量裝載器

  • 如果需要執行的方法較多,代碼重複度會很高,不利於代碼維護。
  • 使用第二種方法進行批量操作,使用 addTests() 方法,
  • addTests() 方法用於接收一組測試方法的集合,可以一次性將對象全部測試方法添加至測試套件中
  • 創建腳本如下:
import unittest
from MyTestCase1 import TestCase1
from MyTestCase2 import TestCase2
#實例化一個測試套件
suite = unittest.TestSuite()

#第二種裝載器:批量添加多個測試方法,這裏封裝為一個列表對象
cases = [TestCase1("test_1"), 
        TestCase1("test_2"),
        TestCase2("test_3"), 
        TestCase2("test_4")]
        
suite.addTests(cases)
#實例化執行器,執行測試套件需要使用測試執行器
runner = unittest.TextTestRunner()
#執行測試套件
runner.run(suite)
  • 執行結果如下,可見效果一樣:
PS E:\pytest_test_selenium> python -u "e:\pytest_test_selenium\TestSuite_runner.py"
This is TestCase1 test_1
.This is TestCase1 test_2
.This is TestCase2 test_3
.This is TestCase2 test_4
.
----------------------------------------------------------------------
Ran 4 tests in 0.001s

OK

2.5.3 用例級裝載器

  • 可以從測試用例維度將測試用例中的全部方法一次裝入測試套件
  • 腳本代碼如下:
import unittest
from MyTestCase1 import TestCase1
from MyTestCase2 import TestCase2
#實例化一個測試套件
suite = unittest.TestSuite()
#第三種裝載器:按照測試用例的級別來添加

#實例化TestLoader對象,調用loadTestsFromTestCase()方法從測試用例中獲取全部可執行測試方法
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestCase2))
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestCase1))

#還可使用loadTestsFromName()方法從模塊中加載測試用例
suite.addTests(unittest.TestLoader().loadTestsFromName("MyTestCase2"))
suite.addTests(unittest.TestLoader().loadTestsFromName("MyTestCase1"))

#實例化執行器,執行測試套件需要使用測試執行器
runner = unittest.TextTestRunner()
#執行測試套件
runner.run(suite)
  • 執行結果如下,執行順序與添加在測試用例中的順序一致:
PS E:\pytest_test_selenium> python -u "e:\pytest_test_selenium\TestSuite_runner.py"
This is TestCase2 test_1
.This is TestCase2 test_2
.This is TestCase2 test_3
.This is TestCase2 test_4
.This is TestCase1 test_1
.This is TestCase1 test_2
.This is TestCase1 test_3
.This is TestCase1 test_4
.
----------------------------------------------------------------------
Ran 8 tests in 0.002s

OK

2.5.4模塊級裝載器

  • 比用例範圍再大一級就是腳本模塊
  • 創建一個包含TestCase1和TestCase2兩個測試用例的代碼模塊(Module)的MyTestCaseModule.py:
import unittest

class TestCase1(unittest.TestCase):
    def test_1(self):
        print("This is TestCase1 test_1")
    def test_2(self):
        print("This is TestCase1 test_2")
    def test_3(self):
        print("This is TestCase1 test_3")
    def test_4(self):
        print("This is TestCase1 test_4")
    
class TestCase2(unittest.TestCase):
    def test_1(self):
        print("This is TestCase2 test_1")
    def test_2(self):
        print("This is TestCase2 test_2") 
    def test_3(self):
        print("This is TestCase2 test_3")
    def test_4(self):
        print("This is TestCase2 test_4")

if __name__ == "__main__":
    unittest.main()
  • 通過模塊級裝載器創建測試套件腳本如下:
import unittest
import MyTestCaseModule
#實例化一個測試套件
suite = unittest.TestSuite()
#第四種裝載器:按照代碼模塊的級別來添加
suite.addTests(unittest.TestLoader().loadTestsFromModule(MyTestCaseModule))
#實例化執行器,執行測試套件需要使用測試執行器
runner = unittest.TextTestRunner()
#執行測試套件
runner.run(suite)
  • 執行結果如下:
PS E:\pytest_test_selenium> python -u "e:\pytest_test_selenium\TestSuite_runner.py"
This is TestCase1 test_1
.This is TestCase1 test_2
.This is TestCase1 test_3
.This is TestCase1 test_4
.This is TestCase2 test_1
.This is TestCase2 test_2
.This is TestCase2 test_3
.This is TestCase2 test_4
.
----------------------------------------------------------------------
Ran 8 tests in 0.002s

OK

2.5.5 目錄級裝載器

  • 比模塊範圍大一級的是文件系統目錄,Unittest測試框架同樣提供了按照目錄遍歷指定模塊並執行全部可執行方法的裝載器----目錄級裝載器
  • 創建一個discover對象,用於遍歷指定目錄(stir_dir)包含的所有模塊代碼,按照指定規則(Pattern)篩選並匹配後執行模塊中所有可執行的測試方法。
  • 遍歷目錄的方法實際上沒有用到測試套件,但是discover和Suite對象作用原理是互通的
  • 腳本如下:
import unittest
import MyTestCaseModule
#實例化一個測試套件
suite = unittest.TestSuite()
#第五種裝載器:遍歷目錄,獲取所有的測試模塊和測試方法
discover = unittest.defaultTestLoader.discover(start_dir='./',pattern='MyTestCase*.py')
#實例化執行器,執行測試套件需要使用測試執行器
suite.addTests(discover)
runner = unittest.TextTestRunner()
#執行測試套件
runner.run(suite)
  • 執行結果如下:
PS E:\pytest_test_selenium> python -u "e:\pytest_test_selenium\TestSuite_runner.py"
This is TestCase1 test_1
.This is TestCase1 test_2
.This is TestCase1 test_3
.This is TestCase1 test_4
.This is TestCase2 test_1
.This is TestCase2 test_2
.This is TestCase2 test_3
.This is TestCase2 test_4
.This is TestCase1 test_1
.This is TestCase1 test_2
.This is TestCase1 test_3
.This is TestCase1 test_4
.This is TestCase2 test_1
.This is TestCase2 test_2
.This is TestCase2 test_3
.This is TestCase2 test_4
.
----------------------------------------------------------------------
Ran 16 tests in 0.004s

OK

2.6 測試執行器(TestRunner)—— 用例的運行與結果展示

測試執行器(TestRunner)負責執行測試套件中的用例,並生成測試結果。Unittest內置TextTestRunner(控制枱輸出),也可通過第三方庫(如HTMLTestRunner)生成HTML格式報告。

2.6.1 基礎執行器:TextTestRunner

TextTestRunner是默認的執行器,通過控制枱輸出用例執行結果,常用參數:

  • verbosity:日誌詳細程度(1:簡潔模式,2:詳細模式)
  • stream:結果輸出位置(默認sys.stderr,可指定文件)
  • 示例代碼(詳細模式+輸出到文件):
import unittest
import sys

class TestDemo(unittest.TestCase):
    def test_success(self):
        self.assertTrue(True)
    
    def test_failure(self):
        self.assertEqual(1, 2)  # 斷言失敗

if __name__ == '__main__':
    suite = unittest.TestLoader().loadTestsFromTestCase(TestDemo)
    # 詳細模式(verbosity=2),結果輸出到test_result.txt
    with open('test_result.txt', 'w') as f:
        runner = unittest.TextTestRunner(verbosity=2, stream=f)
        runner.run(suite)
  • test_result.txt內容:
Ran 2 tests in 0.008s

FAILED (failures=1)


2 != 1

預期:1
實際:2

Traceback (most recent call last):
  File "E:\pytest_test_selenium\lianxi.py", line 10, in test_failure
    self.assertEqual(1, 2)  # 斷言失敗
AssertionError: 1 != 2


進程已結束,退出代碼為 1

----------------------------------------------------------------------
FAILED (failures=1)

2.6.2 擴展執行器:HTMLTestRunner(生成HTML報告)

HTMLTestRunner是第三方庫,可生成美觀的HTML格式報告,需先安裝:pip install html-testRunner

  • 示例代碼:
import unittest
from html_test_runner import HTMLTestRunner

class TestHTML(unittest.TestCase):
    def test_passed(self):
        self.assertEqual("test".upper(), "TEST")
    
    def test_failed(self):
        self.assertIn(1, [2, 3])

if __name__ == '__main__':
    suite = unittest.TestLoader().loadTestsFromTestCase(TestHTML)
    # 生成HTML報告(輸出到reports目錄,報告名含時間戳)
    with open('reports/test_report.html', 'w') as f:
        runner = HTMLTestRunner(
            stream=f,
            title='Unittest測試報告',
            description='用例執行結果詳情'
        )
        runner.run(suite)

執行後會在reports目錄生成HTML文件,包含用例通過率、執行時間、失敗詳情等信息,支持瀏覽器直接打開查看。


2.7 測試報告(TestReport)—— 結果的結構化呈現

想要為Python單元測試生成更直觀、專業的測試報告,瞭解最新的測試報告生成方法確實很重要。下面梳理了從unittest內置方法到多種第三方擴展庫的測試報告生成方案。

📊 測試報告生成方案概覽

首先,用一個表格來彙總幾種主流的測試報告生成方式及其核心特點:

報告類型/庫

輸出格式

易用性

美觀度

額外功能

安裝方式

unittest.TextTestRunner

文本

內置



Python內置

HTMLTestRunner

HTML

中等

⭐⭐

基礎HTML報告

下載.py文件放置於Lib目錄

BeautifulReport

HTML

簡單

⭐⭐⭐⭐

美觀的HTML報告

pip install BeautifulReport

BSTestRunner

HTML

中等

⭐⭐⭐

美化版HTML報告

下載.py文件放置於Lib目錄

unittestreport

HTML

簡單

⭐⭐⭐⭐

重運行、數據驅動、郵件、釘釘

pip install unittestreport

🔧 各方案使用方法與示例

2.7.1 unittest內置文本報告

  • unittest框架自帶的TextTestRunner可以生成基礎文本報告,通過verbosity參數控制詳細程度:
import unittest

# 創建測試套件
suite = unittest.defaultTestLoader.discover('./test_cases', pattern='test_*.py')

# 生成文本報告
with open('./report/test_result.txt', 'w', encoding='utf-8') as f:
    runner = unittest.TextTestRunner(stream=f, verbosity=2, descriptions="測試執行報告")
    runner.run(suite)

2.7.2 HTMLTestRunner:經典HTML報告

HTMLTestRunner是一個經典的HTML報告生成庫,需要單獨下載HTMLTestRunner.py文件並放置在Python的Lib目錄或項目目錄中。

import unittest
import time
import HTMLTestRunner

# 創建測試套件
suite = unittest.defaultTestLoader.discover('./test_cases', pattern='test_*.py')

# 生成帶時間戳的HTML報告
now = time.strftime("%Y-%m-%d_%H_%M_%S")
filename = f'./report/{now}_test_report.html'

with open(filename, 'wb') as f:
    runner = HTMLTestRunner.HTMLTestRunner(
        stream=f,
        title='自動化測試報告',
        description='測試用例執行詳情'
    )
    runner.run(suite)

2.7.3 BeautifulReport:美觀的HTML報告

BeautifulReport生成的報告界面簡潔直觀。

import unittest
from BeautifulReport import BeautifulReport

# 創建測試套件
suite = unittest.defaultTestLoader.discover('./test_cases', pattern='test_*.py')

# 生成BeautifulReport
runner = BeautifulReport(suite)
runner.report(
    description='全量測試用例',  # 報告描述
    filename='beautiful_report',  # 報告文件名
    report_dir='./report'  # 報告保存目錄
)

2.7.4 BSTestRunner:另一種美化選擇

BSTestRunner是在HTMLTestRunner基礎上美化的版本,使用方式與HTMLTestRunner類似。

import unittest
import BSTestRunner

# 創建測試套件
suite = unittest.defaultTestLoader.discover('./test_cases', pattern='test_*.py')

# 生成BSTestRunner報告
with open('./report/bs_test_report.html', 'wb') as f:
    runner = BSTestRunner.BSTestRunner(
        stream=f,
        title='App自動化測試報告',
        description='測試用例執行情況'
    )
    runner.run(suite)

2.7.5 unittestreport:功能全面的新選擇

unittestreport是一個功能豐富的第三方庫,除了生成美觀的HTML報告,還支持失敗重跑、數據驅動、郵件和釘釘通知等實用功能。

pip install unittestreport
  • 基礎報告生成:
import unittest
from unittestreport import TestRunner

# 創建測試套件
suite = unittest.defaultTestLoader.discover('./test_cases')

# 執行測試並生成報告
runner = TestRunner(
    suite,
    filename="unittestreport_test.html",
    report_dir="./report",
    title=" unittestreport測試報告",
    tester="測試員",
    templates=1  # 報告模板主題,可選1, 2, 3
)
runner.run()
  • 失敗重跑功能:
# 方法一:使用裝飾器標記需要重跑的用例
from unittestreport import rerun

class TestDemo(unittest.TestCase):
    @rerun(count=3, interval=2)  # 失敗重跑3次,間隔2秒
    def test_case(self):
        # 測試代碼
        pass

# 方法二:全局重跑
runner = TestRunner(suite)
runner.rerun_run(count=3, interval=2)  # 所有失敗用例重跑3次[citation:8]
  • 數據驅動:
from unittestreport import ddt, list_data, json_data, yaml_data

@ddt
class TestClass(unittest.TestCase):
    # 使用列表數據驅動
    cases = [{'title': '用例1', 'data': '參數', 'expected': '預期結果'}]
    
    @list_data(cases)
    def test_case(self, data):
        # 測試代碼
        pass
    
    # 使用JSON文件數據驅動
    @json_data("cases.json")
    def test_json_case(self, data):
        pass
  • 發送郵件通知:
# 執行測試後發送郵件
runner.run()
runner.send_email(
    host="smtp.qq.com",
    port=465,
    user="sender@qq.com",
    password="授權碼",  # 注意是SMTP授權碼,非郵箱登錄密碼
    to_addrs="receiver@qq.com"
)

💡 測試報告優化技巧

  1. 為用例添加詳細描述:在測試類和測試方法下使用三引號添加文檔字符串,這些內容會顯示在HTML報告中。
class TestDemo(unittest.TestCase):
     """這是一個測試類的描述"""
     
     def test_case(self):
         """這是一個測試方法的詳細描述"""
         self.assertTrue(True)
  1. 動態報告命名:使用時間戳為報告文件命名,防止覆蓋。
import time
  timestamp = time.strftime("%Y-%m-%d_%H_%M_%S")
  filename = f'./report/test_report_{timestamp}.html'
  1. 結合日誌系統:配合Python的logging模塊記錄詳細執行過程。
import logging
 
 logging.basicConfig(
     level=logging.INFO,
     format='%(asctime)s %(levelname)s %(message)s',
     filename='test.log'
 )

💎 如何選擇

  • 對於快速驗證或簡單項目,使用內置的TextTestRunner。
  • 需要基礎的HTML報告且環境受限(無法pip安裝)時,選擇HTMLTestRunner或BSTestRunner。
  • 追求報告美觀度和易用性,BeautifulReport是個不錯的選擇。
  • 需要全面的測試功能(如失敗重跑、數據驅動、多種通知方式),推薦使用unittestreport。

三、六大核心組件聯動邏輯

Unittest的六大組件並非孤立存在,而是通過以下流程協同工作:

  • 定義用例:通過TestCase子類編寫test_方法,結合setUp/tearDown(夾具)初始化環境。
  • 組織用例:通過TestSuite手動組合用例,或用TestLoader自動發現用例。
  • 執行用例TestRunner調用套件中的用例,執行過程中觸發夾具的前置/後置操作,通過斷言判斷結果。
  • 生成報告TestRunner將執行結果格式化(文本/HTML/XML),生成TestReport

簡言之:用例是基礎,夾具是環境保障,套件是組織方式,加載器是批量工具,執行器是運行核心,報告是結果呈現