單元測試----Unittest框架

一: Unittest核心要素:

1:核心要素概念

  • TestCase:測試用例

TestCase繼承於 unittest.TestCase
測試方法必須以test開頭html

  • TestSuit:測試套件

測試套件: 把多個測試用例集成在一塊兒就是測試套件。
1:實例化測試套件 suite = unittest.TestSuite()
2: 將測試用例加入測試套件 suite.addTest(MyTest(‘test_xxx’))python

  • TextTestRunner:

測試執行器: 用來執行測試套件
1: 實例化測試執行器: runner = unittest.TextTestRunner()
2: 執行測試套件 : runner.run(suite)數組

  • FixTure:概述:

測試用例類中實現了前置和後置方法,則這個測試類就是一個Fixture。
例如實現了: setUp() 和 tearDownide

2:測試流程圖:

在這裏插入圖片描述

3: 案例演示執行流程:

# 1: 導包
import unittest

# 2: 準備測試類
class MyTest(unittest.TestCase):
    def test_1(self):
        print("這是:test_1")

    def test_2(self):
        print("這是:test_2")


if __name__ == '__main__':

    # 3: 將測試用例添加到 測試套件中。
    # 實例化測試套件
    suite = unittest.TestSuite()
    # 將測試用例添加到測試套件中。
    suite.addTest(MyTest('test_1'))
    suite.addTest(MyTest('test_2'))

    # 4:運行容器中的測試用例
    # 實例化測試執行器
    runner = unittest.TextTestRunner()
    runner.run(suite)

演示:
在這裏插入圖片描述函數

4: defaultTestLoader:指定目錄下測試用例自動添加到測試套件中

# 1: 導包
import unittest

# 2: 準備測試類
class MyTest(unittest.TestCase):
    def test_1(self):
        print("這是:test_1")

    def test_2(self):
        print("這是:test_2")


if __name__ == '__main__':

    # 將當前目錄下的test_1.demo.py中的測試用例所有加載到測試套件。
    suite = unittest.defaultTestLoader.discover("./", "test_1_demo.py")
    # 運行測試用例
    runner = unittest.TextTestRunner()
    runner.run(suite)

二: unittest的使用

1:unittest使用步驟

  • unittest默認加載腳本的順序是:根據ASCII碼的順序加載,數字與字母的順序爲:0-9,A-Z,a-z
""" unittest的基本使用"""

# 1: 導入unittest包
import unittest

# 2: 建立測試用例類
class MyTest(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        print("setUpClass")

    @classmethod
    def tearDownClass(cls):
        print("tearDownClass")

    def setUp(self):
        print("setup")

    def tearDown(self):
        print("tearDown")

    def test_s(self):
        print("test_s")
        a = 1 + 1
        self.assertEqual(a, 2, "結果不爲2")

    def test_b(self):
        print("test_b")
        b = 1 + 2
        self.assertEqual(b, 2, "結果不爲2")

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

運行結果:單元測試

在這裏插入圖片描述

2: unittest提供的斷言函數[查表]

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

3:參數化:

問題: 若是一個測試用例須要傳遞參數,怎麼傳遞呢??
1: 安裝包:測試

pip install parameterized -i https://pypi.tuna.tsinghua.edu.cn/simple

2: 案例:ui

# 1:導包
import unittest
from parameterized import parameterized

# 2: 建立測試類
class MyTest(unittest.TestCase):

    @parameterized.expand([('renshanwen', 23), ('niuniu', 25)])
    def test_param(self, name, age):
        print("name: %s, age: %s" % (name, age))


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

3: 運行效果:
在這裏插入圖片描述spa

三: mock的使用:

1: mock的原理:

Mock是Python中一個用於支持單元測試的庫,它的主要功能是使用mock對象替代掉指定的Python對象,以達到模擬對象的行爲。
在這裏插入圖片描述code

2:mock的基本使用

return_value的使用

案例: 業務需求: 因爲一段代碼尚未實現,可是此時須要返回一個250的數字,此時須要使用mock代替未實現的代碼,返回一個250的數字,目的是讓測試流程跑通。

# 1:導包
import unittest
import unittest.mock

# 2: 建立模擬的類
class MyTest(unittest.TestCase):

    def test_return(self):
        # 建立一個能返回250的可調用對象
        mock_obj = unittest.mock.Mock(return_value=250)
        # 調用mocke對象,拿到返回值
        ret = mock_obj()
        print(ret)

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

在這裏插入圖片描述

side_effect的使用:

1: 利用side_effect傳遞一個異常:

# 1:導包
import unittest
import unittest.mock

# 2: 建立模擬的類
class MyTest(unittest.TestCase):

    def test_return(self):
        # 建立一個能返回250的可調用對象
        mock_obj = unittest.mock.Mock(side_effect=BaseException("自定義異常"))
        # 調用mocke對象,拿到返回值
        ret = mock_obj()
        print(ret)

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

在這裏插入圖片描述
2:使用side_effect傳遞一個數組:

import unittest.mock

# 2: 建立模擬的類
class MyTest(unittest.TestCase):

    def test_return(self):
        # 建立一個能返回250的可調用對象
        mock_obj = unittest.mock.Mock(side_effect=[1, 2, 3])
        # 調用mocke對象,拿到返回值
        for i in range(3):
            print(mock_obj())


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

在這裏插入圖片描述
3: 使用side_effect傳遞一個函數:

# 1:導包
import unittest
import unittest.mock

# 2: 建立模擬的類
class MyTest(unittest.TestCase):
    def test_return(self):
        def sum_ab(a, b):
            return a + b
        # 建立一個能返回250的可調用對象
        mock_obj = unittest.mock.Mock(side_effect=sum_ab)
        # 調用mocke對象,拿到返回值
        print(mock_obj(1, 2))

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

在這裏插入圖片描述

mock的案例:

業務需求: 如今有一個需求: A負責開發支付功能,可是A尚未開發完成。B負責支付狀態模塊,已經完成,B代碼中調用A寫的代碼的支付函數了。如今要求C測試B寫的代碼是否符合要求,C如何測試??

答: 使用mock替換A寫的函數,而後調用B寫的函數的時候就會調用咱們的mock,不會調用A未完成的代碼。
在這裏插入圖片描述

import unittest
import unittest.mock
import pay
import pay_status

class TestPay(unittest.TestCase):
    def test_success(self):
        # 用一個mock來代替pay中的pay_way函數
        pay.pay_way = unittest.mock.Mock(return_value={"result": "success", "reason":"null"})
        # 盜用要測試的函數
        ret = pay_status.pay_way_status() 
        self.assertEqual(ret, '支付成功', '支付失敗')

    def test_fail(self):
        pay.pay_way = unittest.mock.Mock(return_value={"result": "fail", "reason":"餘額不足"})

        ret = pay_status.pay_way_status() # 調用支付狀態函數
        self.assertEqual(ret, '支付失敗', '測試失敗')

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

在這裏插入圖片描述

mock的反作用:

提示: 當咱們使用 mock 掉一個對象後,默認狀況下,在後面執行的代碼都會受到影響,好比其餘的測試用例,其餘的代碼,只要是這些代碼是在 mock 以後執行的都會受影響。

import unittest
import unittest.mock
import pay
import pay_status

class TestPay(unittest.TestCase):
    def test_success(self):
        # 用一個mock來代替pay中的pay_way函數
        pay.pay_way = unittest.mock.Mock(return_value={"result": "success", "reason":"null"})
        # 盜用要測試的函數
        ret = pay_status.pay_way_status()
        self.assertEqual(ret, '支付成功', '支付失敗')

    def test_success2(self):
        # 盜用要測試的函數
        ret = pay_status.pay_way_status()
        self.assertEqual(ret, '支付成功', '支付失敗')

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

在這裏插入圖片描述

對於上面的代碼,test_success2,咱們並無使用mock來代替,結果也使用了mock。緣由就是mock一旦被使用,後面執行的測試用例只能使用mock,不能使用原來的函數了。

限制mock只做用於當前函數的兩種方式

1: patch裝飾器:

1: 方法上面加上裝飾器: @mock.patch(‘被代替的函數路徑’)
2: 函數另外增長一個傳入值。
3: 函數內部直接使用 傳入值的名字.return_value = {返回值鍵值對}

import unittest
from unittest import mock

import pay
import pay_status

class TestPay(unittest.TestCase):

    @mock.patch('pay.pay_way')
    def test_success(self, mock_pay_way):
        # 用一個mock來代替pay中的pay_way函數
        mock_pay_way.return_value={"result": "success", "reason":"null"}
        # 盜用要測試的函數
        ret = pay_status.pay_way_status()
        self.assertEqual(ret, '支付成功', '支付失敗')

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

2: patch上下文管理器:

1: 函數內部使用 : with mock.patch(‘被替代的函數路徑’) as 別名:
2: 別名.return_value = {返回值鍵值對}

import unittest
from unittest import mock

import pay
import pay_status

class TestPay(unittest.TestCase):
    def test_success(self):
        with mock.patch('pay.pay_way') as mock_pay_way:
            # 用一個mock來代替pay中的pay_way函數
            mock_pay_way.return_value={"result": "success", "reason":"null"}
            # 盜用要測試的函數
            ret = pay_status.pay_way_status()
            self.assertEqual(ret, '支付成功', '支付失敗')

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

類方法的替換:

  • mock.patch.object(類, ‘方法名’)返回指定類的函數的mock對象
    剛剛上面被替換的都是一些函數,若是想要替換一個類中的某個函數如何操做呢???
from unittest import mock
import unittest

class Pay(object):
    def pay_way(self):
        """假設這裏是一個支付的功能,未開發完 支付成功返回:{"result": "success", "reason":"null"} 支付失敗返回:{"result": "fail", "reason":"餘額不足"} reason返回失敗緣由 """
        raise NotImplementedError('代碼尚未實現')

    def pay_way_status(self):
        """根據支付的結果success或fail,判斷跳轉到對應頁面 假設這裏的功能已經開發完成"""

        # todo 此時pay_way()函數並未完成!你先假定他完成了
        result = self.pay_way()
        print(result)

        if result["result"] == "success":
            return "支付成功"
        if result["result"] == "fail":
            return "支付失敗"

方案一: 傳統方式:

1: 先實例化這個類
2:對象.被替換函數 = mock.Mock(return_value = {返回的鍵值對})
3:對象.被測函數()

class TestPayStatues(unittest.TestCase):
    '''單元測試用例'''

    def test_success1(self):
        '''測試支付成功場景'''
        p = Pay() # 實例化對象
        p.pay_way = mock.Mock(return_value = {"result": "success", "reason":"null"})
        statues = p.pay_way_status()
        print(statues)
        self.assertEqual(statues, "支付成功")

方案二: 裝飾器

1: 在測試函數上增長裝飾器:@mock.patch.object(類, ‘被代替的方法名’)
2: 在增長一個函數傳值,def test_success2(self, mock_obj):
3: 使用新傳值.return_value = {返回的鍵值對}
4: 實例化類.被測試函數()

class TestPayStatues(unittest.TestCase):
    @mock.patch.object(Pay, 'pay_way')
    def test_success2(self, mock_obj):
        '''測試支付成功場景'''
        mock_obj.return_value = {"result": "success", "reason":"null"}
        # 根據支付結果測試頁面跳轉
        statues = Pay().pay_way_status()
        print(statues)
        self.assertEqual(statues, "支付成功")

方案三:使用whith上下文

1: with mock.patch.object(類, ‘被代替的方法名’) as 新名字
2:新名字.return_value = {}
3: 實例化類.被測試函數()

class TestPayStatues(unittest.TestCase):
    def test_success3(self):
        '''測試支付成功場景'''
        with mock.patch.object(Pay, 'pay_way') as mock_obj:
            mock_obj.return_value = {"result": "success", "reason":"null"}
            # 根據支付結果測試頁面跳轉
            statues = Pay().pay_way_status()
            print(statues)
            self.assertEqual(statues, "支付成功")

mock的兩個屬性:

  • mock.called: 決定mock是否被調用過。
  • mock.call_count : 決定mock被調用的次數。

案例測試:

import unittest
import unittest.mock


class MockTest(unittest.TestCase):

    def test_return_value(self):
        mock_obj = unittest.mock.Mock(return_value=1999)
        result = mock_obj()
        print(result)  # 打印 1999

        print(mock_obj.called)  # 是否被調用過, 返回布爾值
        print(mock_obj.call_count)  # 獲取調用測試, 返回調用測試


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

在這裏插入圖片描述

四: 測試報告

1:HTMLTestRunner

1: 安裝包:

pip install -i https://pypi.tuna.tsinghua.edu.cn/simple htmltestrunner-python3

2:核心代碼:

if __name__ == '__main__':
    # 1. 把測試用例添加到suite容器中
    suite = unittest.defaultTestLoader.discover('./', 'test_1_html.py')

    # 2. 打開文件,是一個文件對象
    with open('./HTMLTestRunner.html', 'w', encoding='utf-8') as f:
        # 3. HTMLTestRunner()建立一個runner對象
        runner = HTMLTestRunner(
            stream=f,  # 測試報告須要寫入到的文件
            verbosity=2,  # 控制檯輸出信息的詳細程度, 默認爲1
            title='這是報告標題',  # 測試報告的標題
            description='這是一個測試報告'  # 測試報告的描述
        )
        # 4. runner把容器中測試用例運行
        runner.run(suite)

2: BeautifulReport

1: 安裝包:

pip install -i https://pypi.tuna.tsinghua.edu.cn/simple beautifulreport

2: 核心代碼:

if __name__ == '__main__':
    # 1. 把測試用例添加到suite容器中
    suite = unittest.defaultTestLoader.discover('./', 'test_2_beautiful.py')

    # 2. 建立runner對象,同時把suite傳參進入
    runner = BeautifulReport(suite)

    # 3. 運行,同時生成測試報告
    # 參數1:生成文件的註釋, 參數2:生成文件的filename, 參數3:生成report的文件存儲路徑
    runner.report('報告描述必須有,在報告中顯示爲用例名稱', '測試報告文件名', './')
相關文章
相關標籤/搜索