美文网首页
Python测试-pytest, 2024-03-09

Python测试-pytest, 2024-03-09

作者: Mc杰夫 | 来源:发表于2024-03-08 13:57 被阅读0次

(2024.03.09 Sat @KLN)

对比Pytest和Unittest

pytest的优势在于简单容易实现,不需要太多test fixture,构建方便,执行测试时会在项目路径(子目录)下自动发现和运行名如test_*.py*_test.py的文件,并在terminal中输入pytest即可运行。

  • 语法和简洁性:pytest提供了比unittest更加简洁(concise)和可读性高的语法,方便写测试和理解
  • 测试发现(test discovery):pytest不需要提供显式和明确的子类和命名规则(naming convention)就可以自动发现测试文件和测试用例;unittest遵循严格的发现机制
  • 固定件(fixtures)和插件(fixtures&plugins):pytest提供了功能强大的fixture mechanism,简化了测试设定和teardown。fixture可用于定义重复使用的设定和code cleanup,提升测试的组织性减少代码重写(duplication)。pytest还提供了大量插件用于扩展功能,诸如test coverage,mocking和参数化(parameterisation)
  • 测试参数化(parameterisation):pytest内置了对参数化的支援,可便于开发者在相同的测试中使用不同的输入和参数,特别适用于重复代码在不同的场景中的情况。unittest则需要更多的手工设定实现参数化。
  • 声明控制(assertion handling):pytest提供了更富有表达力(expressive)和灵活的assertion handling,提供更多的failure message,便于诊断问题
  • 兼容与生态:pytest需要预先安装单提供了丰富的插件生态和社区支持;unittest作为python内置标准库兼容旧版python

(2024.03.10 Sun @KLN)

Pytest的特征

Fixture, mark

Fixture - Managing States and Dependencies

pytest固定件(后文统称fixture)为测试提供了数据,翻倍(test doubles),和状态初始化设定。fixtures作为函数可返回一系列值(a wide range of values)。每个依赖于fixture的测试必须显式的接受fixture作为输入变量。

使用fixture的典型场景:test-driven development (TTD)
试想一个格式转换的案例,函数format_transfer_1讲一个输入JSON转换成list,输入的JSON有三个变量,family_namegiven_namejob_title,输出的list中的每个元素是一个string,格式如<given_name> <family_name>: <job_title>。以TTD的模式开发,可写成如下模式:

# format_transfer.py

def format_transfer_1(people):
    ...  # function body

测试代码如:

# test_format_transfer.py

def test_format_transfer_1():
c
    assert format_transfer_1_list(people) == ["john tompson: professor", 
                                         "dave laurenson: professor"]

之后又开发一个新函数,功能是将同样的JSON数据转换成CSV格式,代码如

# format_transfer.py

def format_transfer_1_list(people):
    ...  # function body

def format_transfer_2_csv(people):
    ...  # function body

测试代码如

# test_format_transfer.py

def test_format_transfer_1():
    ...

def test_format_transfer_2():
    people = [{"given_name": "john", "family_name": "thompson", "job_title": "professor"},
              {"given_name": "dave", "family_name": "laurenson", "job_title": "professor"}]
    assert format_transfer_2_csv(people) == 
        "given_name, family_name, job_title
         john, thompson, professor
         dave, laurenson, professor"

注意到不同的test cases都用到了相同的测试数据people。在这种情况下,pytest fixture就有了用武之地。测试用数据people置于单独一个函数中,并用@pytest.fixture装饰,在test cases中调用即可。

# test_format_transfer.py

@pytest.fixture
def example_people():
    return people = [{"given_name": "john", "family_name": "thompson", "job_title": "professor"},
                     {"given_name": "dave", "family_name": "laurenson", "job_title": "professor"}]

# ...

在test cases中使用时只需要将函数名(function reference)作为参数传入测试函数,注意到传递的是fixture function reference,而非调用fixture function

# test_format_transfer.py

# ...
def test_format_transfer_1(example_people):
    assert format_transfer_1_list(example_people) == 
        ["john tompson: professor", "dave laurenson: professor"]

def test_format_transfer_2(example_people):
    assert format_transfer_2_csv(example_people) == 
        "given_name, family_name, job_title
         john, thompson, professor
         dave, laurenson, professor"

这种调用方式,类似class@property的调用方式。

fixture的使用减少了代码量,很难不过度使用,下面介绍在何种情况下避免使用fixture。

何时避免使用Fixture
不同的测试用例需要对数据做微小修改,这种情况使用fixture未必能节省代码。如果对数据加入不同的层级未必有效。

成规模使用fixture (use fixture at scale)
随着测试中会用的fixture越来越多,fixture的抽象会提升效率。在pytest中,fixture可以像包(modular)一样被导入,也可以导入其他包,其他包也可以来fixture。这个特性使开发和可以为test cases编写合适的fixture抽象集。
(2024.03.14 Thur)

比如两个在独立文件中的fixtures使用共同的依赖,这种情况就可以转移fixtures到一个更通用的fixture相关的包中,之后在测试文件中引入fixtures。

另有方法可以在整个项目中使用fixture而不引入(import),设置一个设置包conftest.py可实现该功能。

pytest在每个路径中搜索conftest.py包,将通用目的(general-purpose)的fixtures加入到一个conftest.py包中,可在该包母目录(parent directory)和子目录下调用而无需引入,这极大的方便了fixtures的使用。

Fixtures和conftest.py的另一个应用是保护对资源的使用(guarding access to resources)。一个处理API调用的测试集(test suite)中,开发者要确保该测试集不会接入真实网络调用。

相关文章

网友评论

      本文标题:Python测试-pytest, 2024-03-09

      本文链接:https://www.haomeiwen.com/subject/xioezdtx.html