使用Pytest进行单元测试的初学者指南

文摘   2024-10-16 12:01   重庆  

编译整理|TesterHome社区

作者|Stanley Ulili



1.引言


在软件开发过程中,测试是确保代码质量的关键环节。Python 拥有众多测试框架,而 pytest 是其中最受欢迎的之一。它以简洁的语法、强大的功能和丰富的插件生态系统而著称。本指南将深入介绍 pytest,帮助你掌握如何使用它来高效地测试 Python 代码。

2. 安装 pytest

安装 pytest 非常简单。如果你使用的是 Python 的虚拟环境(强烈推荐),首先激活虚拟环境,然后使用以下命令安装:

pip install pytest


如果你想安装特定版本的 pytest,可以在命令中指定版本号,例如:

pip install pytest==5.4.3


3. 第一个 pytest 测试用例


让我们从一个简单的例子开始,了解 pytest 是如何工作的。创建一个名为test_example.py的文件,内容如下:


def add(a, b):    return a + b
def test_add(): assert add(1, 2) == 3


在这个例子中,我们定义了一个add函数,然后编写了一个名为test_add的测试用例。测试用例使用assert语句来验证add函数的输出是否符合预期。要运行这个测试用例,在命令行中进入包含test_example.py文件的目录,然后运行:


pytest


如果一切正常,你将看到类似以下的输出:


============================= test session starts =============================platform linux -- Python 3.6.9, pytest-5.4.3, py-1.8.1, pluggy-0.13.1root@5605836111362047397950633459331456893668-ubuntucollecting... donecollected 1 item
test_example.py.
============================== 1 passed in 0.01s ==============================


这里的.表示测试通过。如果测试失败,将会显示详细的错误信息。


4. pytest 的基本概念


4.1 测试函数


pytest 通过识别以test_开头的函数名来确定测试函数。在上面的例子中,test_add就是一个测试函数。你可以在一个文件中定义多个测试函数,它们将按照顺序依次被测试。


4.2 断言


断言是测试用例中最重要的部分之一。它用于验证代码的输出是否符合预期。在 pytest 中,通常使用assert语句来进行断言。例如:


def test_something():    value = some_function()    assert value == expected_value


如果value不等于expected_value,测试将失败,并显示详细的错误信息。


4.3 夹具(Fixtures)


夹具是 pytest 中一个非常重要的概念。它用于为测试用例提供一些前置条件和后置条件。例如,你可能需要在测试用例中使用一个数据库连接,夹具可以在测试开始前创建这个连接,并在测试结束后关闭它。夹具可以通过@pytest.fixture装饰器来定义,如下所示:


@pytest.fixturedef database_connection():    connection = create_database_connection()    yield connection    connection.close()


在这个例子中,database_connection是一个夹具,它创建了一个数据库连接,并在测试结束后关闭它。在测试用例中,可以使用这个夹具,如下所示:


def test_database_operation(database_connection):    result = do_satabase_operation(database_connection)    assert result == expected_result


夹具可以有不同的作用域,例如函数级、类级、模块级和会话级。不同的作用域决定了夹具在测试过程中的重复使用方式。


4.4 测试模块和测试套件


一个测试模块是一个包含测试函数和夹具的 Python 文件。多个测试模块可以组成一个测试套件。你可以使用pytest命令来运行一个测试套件,它将自动收集所有的测试模块并进行测试。


4.5 标记(Markers)


标记是 pytest 中用于分类和筛选测试用例的一种手段。你可以使用@pytest.mark装饰器来给测试用例加上标记,例如:


@pytest.mark.slowdef test_slow_function():    # 执行一个耗时的操作    pass


在这个例子中,test_slow_function被标记为slow。你可以使用pytest -m slow命令来只运行被标记为slow的测试用例。


5. 编写更复杂的测试用例


5.1 参数化测试


参数化测试是一种将测试用例中的某些参数进行变化的方法,从而可以用一个测试函数测试多个情况。例如,我们可以对上面的add函数进行参数化测试,如下所示:


import pytest
@pytest.mark.parametrize("a,b,expected", [(1, 2, 3), (2, 3, 5), (3, 4, 7)])def test_add(a, b, expected): assert add(a, b) == expected


在这个例子中,我们使用@pytest.mark.parametrize装饰器对test_add函数进行了参数化。参数化的参数为a、b和expected,分别对应add函数的两个输入参数和预期输出。通过这种方式,我们可以用一个测试函数测试add的多个输入输出组合。


5.2 处理异常


有时候我们需要测试代码在出现异常时的行为。在 pytest 中,可以通过@pytest.mark.raises装饰器来测试异常,如下所示:



import pytest
@pytest.mark.raises(Exception)def test_division(): with open('non-existent-file.txt', 'r') as f: content = f.read()


在这个例子中,test_division被标记为raises,并且期望在执行代码时出现Exception类型的异常。如果没有出现异常或者出现的异常类型不正确,测试将失败。


5.3 结合夹具和参数化测试


我们可以将夹具和参数化测试结合起来,以创建更复杂的测试用例。例如,假设我们有一个夹具database_connection和一个参数化测试函数test_database_operation,我们可以这样结合它们:


@pytest.mark.parametridef test_database_operation(database_connection, a, b, expected):    result = do_database_operation(database_connection, a, b)    assert result == expected


在这个例子中,test_database_operation既使用了夹具database_connection,又使用了参数化测试的参数a、b和expected。通过这种方式,我们可以创建出更复杂的测试用例。


6. 测试文件和测试目录结构


在实际项目中,我们需要合理地安排测试文件和测试目录结构。一般来说,我们可以按照以下原则进行安排:


  • 对于一个 Python 项目,通常在项目根目录下创建一个tests目录。

  • 在tests目录下,可以根据项目的模块结构进一步细分测试目录。例如,如果项目有module1和module2两个模块,那么可以在tests目录下创建test_module1和test_module2两个子目录。

  • 在每个测试子目录中,放置与该子目录对应的模块的测试文件。例如,在test_module1目录中放置test_module1.py文件。


通过这种结构,我们可以更方便地组织和管理测试文件,并且在运行测试时,pytest也更容易找到所有的测试文件。


7. 常见问题解答


7.1 如何知道我的测试是否有效?


如果测试通过,在运行测试时你会看到一个.表示通过。如果测试失败,会显示详细的错误信息,你可以根据这些信息来判断测试是否有效以及找出问题所在。


7.2 如何处理测试中的数据?


你可以使用夹具来处理测试中的数据。夹具可以创建模拟数据,也可以获取真实数据。例如,你可以定义一个夹具来创建一个数据库连接,然后通过这个连接获取真实数据,或者定义一个夹具来创建模拟数据,如上面提到的database_connection和fake_data夹具。


7.3 如何筛选特定的测试用例?


你可以使用标记来筛选特定的测试用例。例如,你可以使用pytest -m slow命令来只运行被标记为slow的测试用例。你也可以使用@pytest.mark装饰器给测试用例加上其他标记,然后根据这些标记来筛选测试用例。


7.4 如何在测试中使用真实数据?


你可以使用夹具来获取真实数据。例如,你可以定义一个夹具来创建一个数据库连接,然后通过这个连接获取真实数据。在测试用例中使用这个夹具,就可以使用真实数据进行测试。


7.5 如何加快测试速度?


可以通过以下几种方式加快测试速度:


  • 减少不必要的测试用例。如果某些测试用例对项目质量影响不大,可以考虑删除它们。

  • 优化测试数据。如果测试数据过多或者过于复杂,可以考虑简化它。

  • 优化测试代码。如果测试代码存在冗余或者效率低下的问题,可以考虑优化它。

  • 并行测试。如果你的计算机有多个核心,可以考虑使用并行测试技术,如pytest-xdist插件,可以同时运行多个测试用例,提高测试效率。


8. 结论


pytest 是一个功能强大、简洁易用的 Python 测试框架。通过本指南,你应该对 pytest 有了一个基本的了解,包括它的安装、基本概念、如何编写复杂的测试用例以及如何组织测试文件和测试目录结构。在实际项目中,熟练掌握 pytest 将有助于提高代码质量和项目的可维护性。希望本指南对你有所帮助,让你在测试 Python 代码时更加得心应手。(原文链接:https://betterstack.com/community/guides/testing/pytest-guide/)



1.自动化测试框架|我们为什么抛弃Selenium选择Playwright

2.十个AI驱动的软件测试自动化工具,你尝试过吗?

3.已开源!一款支持HarmonyOS NEXT系统的UI自动化框架hmdriver2发布

4.测试团队FastGPT实战,解锁AI大模型知识库搭建秘籍

5.MTSC2024上海大会,现场录播视频

6.AI测试|自己搭一个AI Agent玩玩


TesterHome社区
测试之家(TesterHome)由一线测试工程师发起和运营的测试技术社区,社区主旨是公益、开源、分享、落地,紧跟前沿技术趋势,致力于推进软件质量保障与安全,是软件质量保障领域的风向标。我们的理念:Coding Share Show Cool
 最新文章