1. 程式人生 > 其它 >【python】瞭解單元測試框架 unittest 與pytest

【python】瞭解單元測試框架 unittest 與pytest

unittest  


官方參考地址:https://docs.python.org/zh-cn/3/library/unittest.html?highlight=assertequal#module-unittest

1、安裝及匯入

#  unittest是python內建的用於測試程式碼的模組,無需安裝直接匯入使用即可
import unittest

 

2、使用

 注意事項:

  1、測試檔案必須先匯入模組 import unittest    2、測試類必須繼承 unittest.TestCase    3、測試方法必須由test開頭    4、測試類執行統一用unittest.main()

   5、setUpClass和tearDownClass 必須使用@classmethod裝飾器
   6、unittest執行測試用例,預設是根據ASCII碼的順序載入測試用例,數字與字母的順序為:0-9,A-Z,a-z。
# coding:utf-8
import unittest #模組匯入
import requests

'''
    unittest特點:
        1、測試檔案必須先匯入模組 import unittest
        2、測試類必須繼承 unittest.TestCase
        3、測試方法必須由test開頭 
        4、測試類執行統一用unittest.main()
        5、setUpClass和tearDownClass 必須使用@classmethod裝飾器
''' class TestDemo(unittest.TestCase): @classmethod # 所有test執行前執行一次 def setUpClass(cls): print("setUpClass 開始執行測試方法") @classmethod # 所有test執行完執行一次 def tearDownClass(cls): print("tearDownClass 測試方法全部執行完成") # 設定每個test測試開始前需要執行的指令,準備測試環境 def setUp(self):
print("setUp 準備環境") # 設定每個test測試完成後需要執行的指令,清理測試環境 def tearDown(self): print("tearDown 環境復原") # 測試方法 def test_step_one(self): self.assertEqual(6, 6, msg="不通過") def test_step_two(self): # 檢查兩個值的相等性; self.assertEqual([1, 2, 3], [1, 2, 3], "斷言比對不一致") # 斷言1和1是否相等 def test_step_three(self): # 比較測試值與true self.assertTrue(True, msg="失敗原因:比對不一致") # def test_step_four(self): # self.assertEquals([1, 2, 3], [3, 2, 1], msg="比對不一致") if __name__=="__main__": # 提供了一個測試指令碼的命令列介面,用來測試繼承unittest.TestCase的類中以 test 開頭的測試用例 unittest.main()
TestDemo

  執行結果

  

 

unittest.main(verbosity=0)  簡潔,只能獲得總的測試用例數和總的結果
unittest.main(verbosity=1)  預設,每個成功的用例前面有個“.” ,每個失敗的用例前面有個 “E”
unittest.main(verbosity=2)  詳情,會顯示每個測試用例的所有相關的資訊

            

  

3、裝飾器

  @ 是修飾符

跳過測試用例  [不會執行被修飾器修飾的方法,類,setup等]

@unittest.skip(reason)
跳過被此裝飾器裝飾的測試。 reason 為測試被跳過的原因。

@unittest.skipIf(condition, reason)
當 condition 為真時,跳過被裝飾的測試。

@unittest.skipUnless(condition, reason)
跳過被裝飾的測試,除非 condition 為真。

@unittest.expectedFailure
將測試標記為預期的失敗或錯誤。 如果測試失敗或在測試函式自身(而非在某個 test fixture 方法)中出現錯誤則將認為是測試成功。 如果測試通過,則將認為是測試失敗。

exception unittest.SkipTest(reason)
引發此異常以跳過一個測試。

示例:

# coding:utf-8
import unittest #模組匯入
import requests

'''
    unittest特點:
        1、測試檔案必須先匯入模組 import unittest
        2、測試類必須繼承 unittest.TestCase
        3、測試方法必須由test開頭 
        4、測試類執行統一用unittest.main()
        5、setUpClass和tearDownClass 必須使用@classmethod裝飾器
'''
class TestDemo(unittest.TestCase):
    # 測試方法
    @unittest.skip("無條件跳過被此裝飾器裝飾的測試: test_step_one")
    def test_step_one(self):
        self.assertEqual(1, 2, "斷言比對不一致")  # 斷言1和1是否相等

    @unittest.skipIf(True, "當 condition 為真時,跳過被裝飾的測試:test_step_two")
    def test_step_two(self):
        # 比較測試值與true
        self.assertEqual(1, 1, "斷言比對不一致")  # 斷言1和1是否相等

    @unittest.skipUnless(False, "跳過被裝飾的測試,除非 condition 為真:test_step_three")
    def test_step_three(self):
        self.assertEqual(1, 2, "斷言比對不一致")  # 斷言1和1是否相等

    @unittest.expectedFailure   # 預期失敗的情況
    def test_step_four(self):
        # 檢查兩個值的相等性;
        self.assertEqual(1, 2, "斷言比對不一致") # 斷言1和1是否相等



if __name__=="__main__":
    # 提供了一個測試指令碼的命令列介面,用來測試繼承unittest.TestCase的類中以 test 開頭的測試用例
    unittest.main()
TestDemo

  執行結果

  

 

 4、斷言

    

 

    

 

 

 

pytest 安裝及使用


官方參考連結:https://docs.pytest.org/en/7.0.x/

1、安裝及匯入

# pytest 是python第三方單元測試框架,相容unittest
pip install pytest

 

2、使用

注意事項 
1、檔名都需要滿足test_*.py格式或*_test.py格式。 2、測試類以Test開頭,並且不能帶有 init 方法,可以包含一個或多個test_開頭的函式 3、直接執行pytest.main():自動查詢當前目錄下,以test_開頭的檔案或者以_test結尾的py檔案 4、斷言使用assert

 

 

1、自動查詢當前目錄下,以test_xxx開頭或者以xxx_test結尾的py檔案
    2、main() 可傳入執行引數及外掛引數(通過[]進行分割,多個引數用逗號分割)
    3、pytest.main(['目錄名'])    # 執行目錄及子目錄下所有用例
    4、pytest.main(['test_xx.py‘]) # 執行指定模組所有用例
    5、pytest.main(['test_xx.py::TestClass::test_def'])    # 指定模組、類、用例(方法)
    6、其他引數配置
        -m=xxx: 執行打標籤的用例
        -reruns=xxx,失敗重新執行
        -q: 安靜模式, 不輸出環境資訊
        -v: 豐富資訊模式, 輸出更詳細的用例執行資訊
        -s: 顯示程式中的print/logging輸出
        --resultlog=./log.txt 生成log
        --junitxml=./log.xml 生成xml報告
pytest.main() 引數

 

 

pytest執行中會出現6個退出code清單

code 0 全部用例執行通過
code 1 全部用例執行完成,存在失敗用例
code 2 測試執行過程中人為中斷
code 3 測試執行過程發生了內部錯誤
code 4 pytest 命令列使用錯誤
code 5 沒有發現可用的測試用例檔案

 

執行pytest

# 執行當前目錄下的所有用例
> pytest
#==== 2 failed, 4 passed, 3 skipped, 1 xfailed in 0.32s ====

# 執行指定模組
> pytest test_run_case_pytest.py
#2 failed, 3 skipped, 1 xfailed in 0.26s


# 執行指定模組下的指定用例
> pytest test_run_case_pytest.py::TestPy::test_funski 
#==== 1 skipped in 0.02s ====

# 模糊匹配包含func用例
> pytest -k func test_run_case_pytest.py
#====  2 failed, 4 deselected in 0.25s ====

 

 

3、斷言

  Pytest使用的是python自帶的關鍵字assert來斷言【格式:assert 表示式,“預期失敗資訊”】結果為True 用例成功,False 用例失敗

 

 

4、修飾器

 

pytest.skip

# pytest.skip (用於函式內,跳過測試用例)
    def test_funski(self):
        for i in range(9):
            if i > 3:
                pytest.skip("函式內跳過測試")
            pass

 

 

 

執行結果:

 

 

 

    @pytest.mark.skip   # 無條件跳過用例(方法)
    def test_case1(self):
        print("\n")
        assert 2 + 3 == 6, "結果比對不符合要求"

 

 執行結果:

 

 

 

    @pytest.mark.skipif(condition="2>0", reason="條件成立 跳過測試")
    def test_print(self):
        print("print列印日誌")

 

執行結果:

 

  @pytest.mark.xfail(True, reason="reason")   # 跳過預期失敗用例,當condition =True 且實際失敗則跳過用例
    def test_case2(self):
        assert add(14, 7) == 20, "不滿足預期結果"

 

執行結果:

 

 


 

 

# coding:utf-8
import pytest
from pawd.base.Logger import *

def add(a:int, b:int=0):
    return a+b

class TestPy():
    '''
        1、檔名都需要滿足test_*.py格式或*_test.py格式。
        2、測試類以Test開頭,並且不能帶有 init 方法,可以包含一個或多個test_開頭的函式
        3、直接執行pytest.main():自動查詢當前目錄下,以test_開頭的檔案或者以_test結尾的py檔案
    :return:
    '''
    def test_func(self):
        raise IOError("E")    # raise 指定異常的型別 IOError

    def test_myfunc(self):
        # 使用pytest去斷言異常的型別 ,異常一致則用例通過,否則用例敗
        with pytest.raises(SystemError):
            self.test_func()

    def test_logs(self):
        logs.info("logs列印日誌")


    def test_print(self):
        print("print列印日誌")

    @pytest.mark.skip   # 無條件跳過用例
    def test_case1(self):
        print("\n")
        assert 2 + 3 == 6, "結果比對不符合要求"


    @pytest.mark.flaky(reruns=5, reruns_delay=2)
    def test_case2(self):
        for i in range(9):
            # assert add(14, i) == 20, "不滿足預期結果"
            try:
                assert add(14, i) == 20, "不滿足預期結果"
            except:
                continue
            else:
                logs.debug(i)


if __name__ == "__main__":
    '''
    1、自動查詢當前目錄下,以test_xxx開頭或者以xxx_test結尾的py檔案
    2、main() 可傳入執行引數及外掛引數(通過[]進行分割,多個引數用逗號分割)
    3、pytest.main(['目錄名'])    # 執行目錄及子目錄下所有用例
    4、pytest.main(['test_xx.py‘]) # 執行指定模組所有用例
    5、pytest.main(['test_xx.py::TestClass::test_def'])    # 指定模組、類、用例(方法)
    6、其他引數配置
        -m=xxx: 執行打標籤的用例
        -reruns=xxx,失敗重新執行
        -q: 安靜模式, 不輸出環境資訊
        -v: 豐富資訊模式, 輸出更詳細的用例執行資訊
        -s: 顯示程式中的print/logging輸出
        --resultlog=./log.txt 生成log
        --junitxml=./log.xml 生成xml報告
    '''
    pytest.main(['-s'])
TestPy類

 

執行結果:

 

 

 

 

 

 

拓展


 單元測試

  定義:是指檢查程式碼的實現和邏輯,針對的是模組、函式、類等

  核心概念:

     test case(測試用例):一個testcase的例項就是一個測試用例。測試前準備環境的搭建(setUp),執行測試程式碼(run),以及測試後環境的還原(tearDown)

    TestSuite(測試套件):多個測試用例集合在一起。

    TestLoader(測試執行器):用來載入Testcase到TestSuite中。

    test fixture(測試環境資料準備和資料清理或者測試腳手架):測試用例環境的搭建和銷燬

    TextTestRunner:用來執行測試用例,其中run(test)會執行TestSuite/TestCase中的run(result)方法。

 

測試目錄結構

  Log:日誌記錄和管理功能,針對不同的情況,設定不同的日誌級別,方便定位問題;

  Report:測試報告生成和管理以及即時通知,測試結果快速響應;

  Source:配置檔案、靜態資源的管理,遵循高內聚低耦合原則;

  Common:公共函式、方法以及通用操作的管理,遵循高內聚低耦合原則;

  TestCase:測試用例管理功能,一個功能點對應一個或者多個case,儘可能的提高覆蓋率;

  TestData:測試資料管理功能,資料與指令碼分離,降低維護成本,提高可移植性;

  TestSuite:測試元件管理功能,針對不同場景不同需求,組裝構建不同的測試框架,遵循框架的靈活性和擴充套件性;

  Statistics:測試結果統計管理功能,每次執行測試的結果統計、分析、對比以及反饋,資料驅動,為軟體優化和流程改進,提供參考;

  Continuous:持續整合環境,即CI環境,包括測試檔案提交、掃描編譯、執行測試、生成報告及時通知等功能,持續整合是自動化測試的核心