如何在 Python 中使用 PyTest 進行單元測試

Python 中編寫代碼時,確保您的代碼按預期運行是非常重要的。做到這一點的最佳方法之一是使用單元測試,這幫助您檢查代碼的小部分(或單元)是否正常工作。

在這篇文章中,我們將學習如何使用 PyTest 編寫和運行有效的單元測試,這是最受歡迎的 Python 測試框架之一。

什麼是單元測試?

單元測試是小型簡單的測試,專注於檢查單個函數或小段代碼。它們幫助確保您的代碼按預期運行,並能及早捕捉錯誤。

可以為代碼的不同部分編寫單元測試,例如函數、方法,甚至類。通過編寫單元測試,您可以在不運行整個程序的情況下測試您的代碼。

為什麼使用 PyTest?

PyTest 是一個流行的 Python 測試框架,使編寫和運行測試變得簡單。

它使用簡單,並擁有許多有用的功能,例如:

  • 它允許您編寫簡單明了的測試用例。
  • 它提供了高級功能,如夾具、參數化測試和插件。
  • 它與其他測試工具和庫兼容良好。
  • 它生成易於閱讀的測試結果和報告。

在 Linux 中設置 PyTest

在我們開始編寫測試之前,我們需要安裝 PyTest。如果您尚未安裝 PyTest,可以使用名為 pip 的 Python 套件管理器進行安裝。

pip install pytest

一旦安裝了 PyTest,您就準備好開始編寫測試了!

使用 PyTest 編寫您的第一個測試

讓我們從編寫一個簡單的函數開始,然後為它編寫測試。

步驟 1:編寫一個簡單的函數

首先,讓我們創建一個我們想要測試的 Python 函數。假設我們有一個將兩個數字相加的函數:

# add.py
def add(a, b):
    return a + b

這是一個簡單的函數,接受兩個數字 a 和 b,將它們相加並返回結果。

步驟 2:為函數編寫測試

現在,讓我們為 add 函數編寫一個測試。在 PyTest 中,測試通常寫在單獨的文件中,通常命名為 test_*.py 以便於識別測試文件。

創建一個名為 test_add.py 的新文件並編寫以下測試代碼:

# test_add.py
from add import add

def test_add_numbers():
    assert add(2, 3) == 5
    assert add(-1, 1) == 0
    assert add(0, 0) == 0

以上代碼的解釋:

  • 我們從 add.py 文件中導入 add 函數。
  • 我們定義了一個名為 test_add_numbers() 的測試函數。在 PyTest 中,測試函數應以 test_ 開頭。
  • 在測試函數內,我們使用assert語句來檢查調用add函數的結果是否與預期值匹配。如果assert語句中的條件為True,則測試通過;否則,測試失敗。

步驟 3:運行測試

要運行測試,打開終端並導航到您的test_add.py文件所在的目錄,然後運行以下命令:

pytest

PyTest將自動找到所有測試文件(以test_開頭的文件)並運行其中的測試。如果一切正常,您應該會看到如下輸出:

Verifying Python Code Functionality

(.)表示測試通過。如果有任何問題,PyTest將顯示錯誤信息。

編寫更高級的測試

現在我們知道如何編寫和運行基本測試,讓我們探索一些PyTest的更高級功能。

測試預期的異常

有時,您希望測試當出錯時您的代碼是否引發了正確的異常。您可以使用pytest.raises()函數來做到這一點。

假設我們想要測試一個將兩個數字相除的函數。如果第二個數字為零,我們希望引發異常(以避免除以零的錯誤)。

這是divide函數:

# divide.py
def divide(a, b):
    if b == 0:
        raise ValueError("Cannot divide by zero")
    return a / b

現在,讓我們為此函數編寫一個測試,以檢查在我們嘗試除以零時是否引發ValueError

# test_divide.py
from divide import divide
import pytest

def test_divide_numbers():
    assert divide(10, 2) == 5
    assert divide(-10, 2) == -5
    assert divide(10, -2) == -5

def test_divide_by_zero():
    with pytest.raises(ValueError):
        divide(10, 0)

代碼解釋:

  • 我們新增了一個測試函數叫做 test_divide_by_zero()
  • 在這個函數內,我們使用 pytest.raises(ValueError) 來檢查當我們以零作為第二個參數來調用除法函數時是否會引發 ValueError

再次使用 pytest 命令運行測試。如果一切正常,你應該會看到這個輸出:

Test Your Code with PyTest

使用 Fixtures 進行設置和清理

在某些情況下,你可能需要在運行測試之前設置某些條件,或在測試完成後進行清理。PyTest 提供了 fixtures 來處理這些情況。

Fixture 是一個函數,你可以用來設置或拆除測試的條件。Fixtures 通常用於創建物件或連接到測試所需的資料庫。

這裡是一個使用 fixture 來設置臨時目錄以進行文件操作測試的例子:

# test_file_operations.py
import pytest
import os

@pytest.fixture
def temporary_directory():
    temp_dir = "temp_dir"
    os.mkdir(temp_dir)
    yield temp_dir  # This is where the test will run
    os.rmdir(temp_dir)  # Cleanup after the test

def test_create_file(temporary_directory):
    file_path = os.path.join(temporary_directory, "test_file.txt")
    with open(file_path, "w") as f:
        f.write("Hello, world!")
    
    assert os.path.exists(file_path)

代碼解釋:

  • 我們定義了一個名為 temporary_directory 的 fixture,它在測試之前創建一個臨時目錄,並在之後刪除它。
  • 測試函數 test_create_file() 使用這個 fixture 在臨時目錄中創建一個文件並檢查該文件是否存在。

再次使用 pytest 命令運行測試。PyTest 將自動檢測並使用這個 fixture。

使用 Pytest 參數化你的測試

有時,你想要用不同的輸入運行相同的測試。PyTest 允許你輕鬆地使用參數化來做到這一點。

假設我們想要測試我們的 add 函數與幾對數字。與其為每一對編寫單獨的測試函數,我們可以使用 pytest.mark.parametrize 來使用不同的輸入運行相同的測試。

# test_add.py
import pytest
from add import add

@pytest.mark.parametrize("a, b, expected", [
    (2, 3, 5),
    (-1, 1, 0),
    (0, 0, 0),
    (100, 200, 300)
])
def test_add_numbers(a, b, expected):
    assert add(a, b) == expected

代碼解釋:

  • 我們使用 pytest.mark.parametrize 裝飾器來定義多組輸入(abexpected)。
  • 測試 function test_add_numbers() 將對每組輸入運行一次。

再次使用 pytest 命令運行測試,這將對每組輸入運行測試四次。

結論

在這篇文章中,我們學會了如何在 Python 中使用 PyTest 寫和運行有效的單元測試,以便及早捕捉錯誤並確保您的代碼按預期工作。

PyTest 使得編寫和運行這些測試變得簡單,隨著您在 Python 旅程中的成長,您可以利用其強大的功能來處理更複雜的測試需求。

Source:
https://www.tecmint.com/unit-testing-python-code-with-pytest/