Python 單元測試

在本教程中,我們將使用 Python 實現單元測試。使用 Python 進行單元測試本身就是一個巨大的話題,但是我們將討論幾個基本概念。

什麼是 Python 單元測試?

單元測試是一種由開發人員自己測試特定模塊來檢查是否有錯誤的技術。單元測試的主要重點是測試系統的單個單元,以分析、檢測和修復錯誤。

Python 提供單元測試模塊來測試源代碼的單元。當我們編寫龐大的代碼時,unittest 起著至關重要的作用,它提供了檢查輸出是否正確的工具。

通常,我們列印該值並將其與參考輸出進行匹配,或者手動檢查輸出。

這個過程需要很多時間。為了克服這個問題,Python 引入了 unittest 模塊。我們還可以通過使用它來檢查應用的性能。

我們將學習如何創建一個基本測試,發現錯誤,並在代碼交付給用戶之前執行它。

測試代碼

我們可以用許多方法測試我們的代碼。在本節中,我們將學習高級方法的基本步驟。

自動化測試與手動測試

人工測試還有另一種形式,稱為探索性測試。這是一次沒有任何計劃的測試。要進行手動測試,我們需要準備一份應用列表;我們輸入不同的輸入,等待預期的輸出。

每次我們給出輸入或更改代碼時,我們都需要仔細檢查列表中的每一個特性並進行檢查。

這是最常見的測試方式,也是一個耗時的過程。

另一方面,自動化測試根據我們的代碼計劃執行代碼,這意味著它運行我們想要測試的代碼的一部分,即我們想要通過腳本而不是人來測試它們的順序。

Python 提供了一套工具和庫來幫助我們為應用創建自動化測試。

單元測試與集成測試

假設我們想檢查汽車的燈光,以及如何測試它們。我們會打開燈走到車外,或者問朋友燈是否亮著。開燈會認為是測試步驟,到外面或者問朋友會知道是測試斷言。在集成測試中,我們可以一次測試多個組件。

這些組件可以是我們代碼中的任何東西,例如我們編寫的函數、類和模塊。

但是集成測試有一個局限性;如果集成測試沒有給出預期的結果怎麼辦。在這種情況下,將很難識別系統的哪個部分正在下降。我們舉前面的例子;如果燈沒亮,電池可能沒電了,blub 壞了,汽車的電腦有故障。

這就是為什麼我們考慮單元測試來了解測試代碼中的確切問題。

單元測試是一個較小的測試,它檢查單個組件是否以正確的方式工作。使用單元測試,我們可以分離出哪些必需品需要在我們的系統中固定。

到目前為止,我們已經看到了兩種類型的測試;集成測試檢查多個組件;其中單元測試檢查應用中的小組件。

讓我們理解下面的例子。

我們將單元測試 Python 內置函數 sum() 應用於已知輸出。我們檢查數字 (2,3,5) 的和()等於 10。


assert sum([ 2, 3, 5]) == 10, "Should be 10"

上面一行將返回正確的結果,因為值是正確的。如果我們傳遞錯誤的參數,它將返回斷言錯誤。例如-


assert sum([1, 3, 5]) == 10, "Should be 10"
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AssertionError: Should be 10

我們可以將上述代碼放入文件中,並在命令行中再次執行。


def test_sum():
    assert sum([2, 3, 5]) == 10, "It should be 10"

if __name__ == "__main__":
    test_sum()
    print("Everything passed")

輸出:

$ python sum.py
Everything is correct

在下面的例子中,為了測試的目的,我們將傳遞元組。創建名為 test_sum2.py. 的新文件

示例- 2:


def test_sum2():
    assert sum([2, 3, 5]) == 10, "It should be 10"

def test_sum_tuple():
    assert sum((1, 3, 5)) == 10, "It should be 10"

if __name__ == "__main__":
    test_sum2()
    test_sum_tuple()
    print("Everything is correct")

輸出:

Everything is correct
Traceback (most recent call last):
  File "<string>", line 13, in <module>
File "<string>", line 9, in test_sum_tuple
AssertionError: It should be 10

解釋-

在上面的代碼中,我們將錯誤的輸入傳遞給了 test_sum_tuple()。輸出與預測結果不同。

上面的方法很好,但是如果有多個錯誤呢?如果遇到第一個錯誤,Python 解釋器會立即給出一個錯誤。為了解決這個問題,我們使用了測試運行器。

選擇測試運行程序

Python 包含許多測試運行程序。最流行的內置 Python 庫叫做 unittest。unittest 可移植到其他框架。考慮以下三個最熱門的測試跑步者。

  • 單元測試
  • 鼻子還是鼻子 2
  • pytest(測試)

我們可以根據自己的要求任意選擇。先簡單介紹一下。

單元測試

unittest 從 2.1 開始就內置在 Python 標準庫中。unittest 最好的一點是,它既有一個測試框架,也有一個測試運行器。unittest 對編寫和執行代碼沒有什麼要求。

  • 代碼必須使用類和函數編寫。
  • 除了內置斷言語句之外,測試用例類中不同斷言方法的序列。

讓我們使用 unittest 用例來實現上面的例子。

示例-


import unittest
class TestingSum(unittest.TestCase):

    def test_sum(self):
        self.assertEqual(sum([2, 3, 5]), 10, "It should be 10")
    def test_sum_tuple(self):
        self.assertEqual(sum((1, 3, 5)), 10, "It should be 10")

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

輸出:

.F
-
FAIL: test_sum_tuple (__main__.TestingSum)
--
Traceback (most recent call last):
  File "<string>", line 11, in test_sum_tuple
AssertionError: 9 != 10 : It should be 10

----------------------------------------------------------------------
Ran 2 tests in 0.001s

FAILED (failures=1)
Traceback (most recent call last):
  File "<string>", line 14, in <module>
  File "/usr/lib/python3.8/unittest/main.py", line 101, in __init__
    self.runTests()
  File "/usr/lib/python3.8/unittest/main.py", line 273, in runTests
    sys.exit(not self.result.wasSuccessful())
SystemExit: True

正如我們在輸出中看到的,它顯示了點(。)執行成功, F 執行失敗。

鼻子

有時,我們需要為應用編寫數百或數千個測試行;這變得很難理解。

機頭測試運行器可以是 unittest 測試運行器的合適替代,因為它與使用 unittest 框架編寫的任何測試兼容。鼻子有兩種——鼻子和鼻頭。我們建議使用 nose2,因為它是最新版本。

使用 nose2,我們需要使用以下命令安裝它。


pip install nose2

在終端中運行以下命令,使用 nose2 測試代碼。


python -m nose2

輸出如下。

FAIL: test_sum_tuple (__main__.TestSum)
--
Traceback (most recent call last):
  File "test_sum_unittest.py", line 10, in test_sum_tuple
    self.assertEqual(sum((2, 3, 5)), 10, "It should be 10")
AssertionError: It should be 10

--
Ran 2 tests in 0.001s

FAILED (failures=1)

nose2 提供了許多命令行標誌來過濾測試。您可以從其官方文檔中了解更多信息。

pytest(測試)

pytest 測試運行器支持執行 unittest 測試用例。 pytest 的實際好處是編寫 pytest 測試用例。pytest 測試用例通常是從 Python 文件開始的方法序列。

pytest 提供以下好處-

  • 它支持內置的 assert 語句,而不是使用特殊的*assert (T1)方法。**
  • 它還為測試用例的清理提供支持。
  • 它可以從最後的案例中重新運行。
  • 它有一個由數百個插件組成的生態系統來擴展功能。

讓我們理解下面的例子。

示例-


def test_sum():
    assert sum([2, 3, 5]) == 10, "It should be 10"
def test_sum_tuple():
    assert sum((1, 2, 5)) == 10, "It should be 10"

寫初試

在這裡,我們將應用我們在前面部分學到的所有概念。首先,我們需要創建一個文件名 test.py 或者任何東西。然後輸入並執行被測試的代碼,捕獲輸出。成功運行代碼後,將輸出與預期結果進行匹配。

首先,我們創建文件 my_sum 文件,並在其中編寫代碼。


def sum(arg):
    total = 0
    for val in arg:
        total += val
    return total

我們初始化了 total 變數,該變數迭代 arg 中的所有值。

現在,我們用下面的代碼創建一個文件名 test.py 。

示例-


import unittest

from my_sum import sum

class CheckSum(unittest.TestCase):
    def test_list_int(self):

        data = [1, 2, 3]
        result = sum(data)
        self.assertEqual(result, 6)

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

輸出:

.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK

說明:

在上面的代碼中,我們從我們創建的 my_sum 包中導入了 sum() 。我們定義了檢查類,繼承了單元測試用例。有一個測試方法- 。test_list_int(),測試整數。

運行代碼後,返回點(。)表示代碼沒有錯誤。

讓我們理解另一個例子。

示例- 2


class Person:
    name1 = []

    def set_name(self, user_name):
        self.name1.append(user_name)
        return len(self.name1) - 1

    def get_name(self, user_id):
        if user_id >= len(self.name1):
            return ' No such user Find'
        else:
            return self.name1[user_id]

if __name__ == '__main__':
    person = Person()
    print('Peter Decosta has been added with id ', person.set_name('Peter'))
    print('The user associated with id 0 is ', person.get_name(0))

輸出:

Peter Decosta has been added with id 0
The user associated with id 0 is Peter

Python 基本函數和單元測試輸出

unittest 模塊產生三種可能的結果。以下是潛在的結果。

  1. OK – 如果所有測試都通過,則返回 OK。
  2. 失敗- 如果任何測試失敗,它將引發評估錯誤異常。
  3. 錯誤- 如果出現任何錯誤而不是斷言錯誤。

讓我們看看下面的基本功能。

| 方法 | 描述 |
| 。資產質量(a,b) | a == b |
| 。assertTrue(x) | 布爾(x)為真 |
| . assertFalse(x) | 布爾(x)為假 |
| 。assertIs(a,b) | a 是 b |
| . assert one(x) | x 是無 |
| 。assertIn(a,b) | b 中的 a |
| 。資產實例(a,b) | 情況(a、b) |
| .斷言概念(a、b) | a 不在 b |
| 。資產通知實例(a,b) | 非情況(a,b) |
| 。斷言者(a,b) | a 不是 b |

Python 單元測試示例


import unittest

# First we import the class which we want to test.
import Person1 as PerClass

class Test(unittest.TestCase):
    """
    The basic class that inherits unittest.TestCase
    """
    person = PerClass.Person()  # instantiate the Person Class
    user_id = []  # This variable stores the obtained user_id
    user_name = []  # This variable stores the person name

    # It is a test case function to check the Person.set_name function
    def test_0_set_name(self):
        print("Start set_name test\n")

        for i in range(4):
            # initialize a name
            name = 'name' + str(i)
            # put the name into the list variable
            self.user_name.append(name)
            # extraxt the user id obtained from the function
            user_id = self.person.set_name(name)
            # check if the obtained user id is null or not
            self.assertIsNotNone(user_id)
            # store the user id to the list
            self.user_id.append(user_id)
        print("The length of user_id is = ", len(self.user_id))
        print(self.user_id)
        print("The length of user_name is = ", len(self.user_name))
        print(self.user_name)
        print("\nFinish set_name test\n")

    # Second test case function to check the Person.get_name function
    def test_1_get_name(self):
        print("\nStart get_name test\n")

        # total number of stored user information
        length = len(self.user_id)
        print("The length of user_id is = ", length)
        print("The lenght of user_name is = ", len(self.user_name))
        for i in range(6):
            # if i not exceed total length then verify the returned name
            if i < length:
                # if the two name not matches it will fail the test case
                self.assertEqual(self.user_name[i], self.person.get_name(self.user_id[i]))
            else:
                print("Testing for get_name no user test")
                # if length exceeds then check the 'no such user' type message
                self.assertEqual('There is no such user', self.person.get_name(i))
        print("\nFinish get_name test\n")

if __name__ == '__main__':
    # begin the unittest.main()
    unittest.main()

輸出:

Start set_name test

The length of user_id is =  4
[0, 1, 2, 3]
The length of user_name is =  4
['name0', 'name1', 'name2', 'name3']

Finish set_name test

Start get_name test

The length of user_id is =  4
The lenght of user_name is =  4
Testing for get_name no user test
.F
======================================================================
FAIL: test_1_get_name (__main__.Test)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:/Users/DEVANSH SHARMA/PycharmProjects/Hello/multiprocessing.py", line 502, in test_1_get_name
    self.assertEqual('There is no such user', self.person.get_name(i))
AssertionError: 'There is no such user' != ' No such user Find'
- There is no such user
+  No such user Find

----------------------------------------------------------------------
Ran 2 tests in 0.002s

FAILED (failures=1)

高級測試場景

在為應用創建測試時,我們必須遵循給定的步驟。

  • 生成必要的輸入
  • 執行代碼,獲取輸出。
  • 將輸出與預期結果匹配。

為字元串或數字等輸入創建靜態值等輸入是一項稍微複雜的任務。有時,我們需要創建一個類或上下文的實例。

我們創建的輸入數據被稱為夾具。我們可以在應用中重用夾具。

當我們重複運行代碼並每次傳遞不同的值並期望相同的結果時,這個過程被稱為參數化。

處理預期故障

在前面的例子中,我們通過整數來測試sum();如果我們傳遞不好的值,比如單個整數或者字元串,會發生什麼?

sum() 會如預期的那樣拋出一個錯誤。這可能是因為測試失敗。

我們可以使用。assertrails()來處理預期的錯誤。在裡面用語句。讓我們理解下面的例子。

示例-


import unittest
from my_sum import sum

class CheckSum(unittest.TestCase):
    def test_list_int(self):

       #  Test that it can sum a list of integers

        data = [1, 2, 3]
        res = sum(data)
        self.assertEqual(res, 6)

    def test_bad_type(self):
        data = "Apple"
        with self.assertRaises(TypeError):
            res = sum(data)

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

輸出:

..
----------------------------------------------------------------------
Ran 2 tests in 0.006s

OK

Python Unittest Ship Test

我們可以使用跳過測試技術跳過單個測試方法或測試用例。在測試結果中,失敗不算失敗。

考慮下面的示例,無條件地跳過該方法。

示例-


import unittest

def add(x,y):
      c = x + y
      return c

class SimpleTest(unittest.TestCase):
   @unittest.skip("The example skipping method")
   def testadd1(self):
      self.assertEquals(add(10,5),7)

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

輸出:

s
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK (skipped=1)

說明:

在上例中, skip() 方法以@token 為前綴。它將一個參數作為日誌消息,我們可以在其中描述跳過的原因。的字元表示測試已成功跳過。

我們可以根據特定的條件跳過特定的方法或塊。

示例- 2:


import unittest

class suiteTest(unittest.TestCase):
    a = 100
    b = 40

    def test_add(self):
        res = self.a + self.b
        self.assertEqual(res, 100)

    @unittest.skipIf(a > b, "Skip because a is greater than b")
    def test_sub(self):

        res = self.a - self.b
        self.assertTrue(res == -10)

    @unittest.skipUnless(b == 0, "Skip because b is eqaul to zero")
    def test_div(self):

        res = self.a / self.b
        self.assertTrue(res == 1)

    @unittest.expectedFailure
    def test_mul(self):

        res = self.a * self.b
        self.assertEqual(res == 0)

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

輸出:

Fsx.
======================================================================
FAIL: test_add (__main__.suiteTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:/Users/DEVANSH SHARMA/PycharmProjects/Hello/multiprocessing.py", line 539, in test_add
    self.assertEqual(res, 100)
AssertionError: 50 != 100

----------------------------------------------------------------------
Ran 4 tests in 0.001s

FAILED (failures=1, skipped=1, expected failures=1)

說明:

正如我們在輸出中看到的,條件 b == 0 且 a>b 為真,因此 test_mul() 方法已跳過。另一方面, test_mul 被標記為預期失敗。

結論

我們已經討論了與 Python 單元測試相關的非常重要的概念。作為初學者,我們需要編寫智能的、可維護的方法來驗證我們的代碼。一旦我們在 Python 單元測試中獲得了一個合適的命令,我們就可以切換到其他框架,比如pytest,並利用更高級的特性。


原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/287458.html

(0)
打賞 微信掃一掃 微信掃一掃 支付寶掃一掃 支付寶掃一掃
小藍的頭像小藍
上一篇 2024-12-23 13:07
下一篇 2024-12-23 13:07

相關推薦

  • Python周杰倫代碼用法介紹

    本文將從多個方面對Python周杰倫代碼進行詳細的闡述。 一、代碼介紹 from urllib.request import urlopen from bs4 import Bea…

    編程 2025-04-29
  • Python計算陽曆日期對應周幾

    本文介紹如何通過Python計算任意陽曆日期對應周幾。 一、獲取日期 獲取日期可以通過Python內置的模塊datetime實現,示例代碼如下: from datetime imp…

    編程 2025-04-29
  • 如何查看Anaconda中Python路徑

    對Anaconda中Python路徑即conda環境的查看進行詳細的闡述。 一、使用命令行查看 1、在Windows系統中,可以使用命令提示符(cmd)或者Anaconda Pro…

    編程 2025-04-29
  • Python中引入上一級目錄中函數

    Python中經常需要調用其他文件夾中的模塊或函數,其中一個常見的操作是引入上一級目錄中的函數。在此,我們將從多個角度詳細解釋如何在Python中引入上一級目錄的函數。 一、加入環…

    編程 2025-04-29
  • Python列表中負數的個數

    Python列表是一個有序的集合,可以存儲多個不同類型的元素。而負數是指小於0的整數。在Python列表中,我們想要找到負數的個數,可以通過以下幾個方面進行實現。 一、使用循環遍歷…

    編程 2025-04-29
  • Python清華鏡像下載

    Python清華鏡像是一個高質量的Python開發資源鏡像站,提供了Python及其相關的開發工具、框架和文檔的下載服務。本文將從以下幾個方面對Python清華鏡像下載進行詳細的闡…

    編程 2025-04-29
  • Python字典去重複工具

    使用Python語言編寫字典去重複工具,可幫助用戶快速去重複。 一、字典去重複工具的需求 在使用Python編寫程序時,我們經常需要處理數據文件,其中包含了大量的重複數據。為了方便…

    編程 2025-04-29
  • 蝴蝶優化演算法Python版

    蝴蝶優化演算法是一種基於仿生學的優化演算法,模仿自然界中的蝴蝶進行搜索。它可以應用於多個領域的優化問題,包括數學優化、工程問題、機器學習等。本文將從多個方面對蝴蝶優化演算法Python版…

    編程 2025-04-29
  • Python程序需要編譯才能執行

    Python 被廣泛應用於數據分析、人工智慧、科學計算等領域,它的靈活性和簡單易學的性質使得越來越多的人喜歡使用 Python 進行編程。然而,在 Python 中程序執行的方式不…

    編程 2025-04-29
  • python強行終止程序快捷鍵

    本文將從多個方面對python強行終止程序快捷鍵進行詳細闡述,並提供相應代碼示例。 一、Ctrl+C快捷鍵 Ctrl+C快捷鍵是在終端中經常用來強行終止運行的程序。當你在終端中運行…

    編程 2025-04-29

發表回復

登錄後才能評論