如何在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:为函数编写测试

现在,让我们为加法函数编写一个测试。在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

使用夹具进行设置和清理

在某些情况下,你可能需要在运行测试之前设置某些条件,或在测试完成后进行清理。 PyTest 提供了夹具来处理这个问题.

夹具是一个函数,你可以用它来设置或拆除测试条件。夹具通常用于创建对象或连接到测试所需的数据库.

以下是一个使用夹具设置临时目录以测试文件操作的示例:

# 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 的夹具,它在测试之前创建一个临时目录,并在之后删除它.
  • 测试函数 test_create_file() 使用这个夹具在临时目录中创建一个文件,并检查该文件是否存在.

再次使用 pytest 命令运行测试。PyTest 将自动检测并使用夹具.

使用 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/