目录支持在多个级别上进行测试参数化允许对夹具函数进行参数化。允许在测试函数或类上定义多组参数和夹具。允许定义自定义参数化方案或扩展。.mark.: 测试函数的参数化.mark. 装饰器是内置的, 它能让你对测试函数的参数做参数化。下面是个典型例子, 它展现了测试函数怎样通过达成检查特定输入是否致使预期输出为目的来做参数化:# test_expectation.py 文件内容 import pytest pytest.mark.parametrize(test_input,expected, [(35, 8), (24, 6), (6*9, 42)]) def test_eval(test_input, expected): assert eval(test_input) expected.mark. 装饰器接收一个参数, 该参数是一个包含元组的列表, 其中每个元组代表一组测试输入以及预期输出它会为 列表里的每一组值都运行一次测试函数每次调用时会把元组中的值分别赋值给 和 变量。如此一来, 函数会针对列表里的每组()值开展一次运行操作, 每一次都会进行评估借助运行eval()函数来实现, 虽说在实际应用情况中应当慎重运用该项函数以此规避安全方面所存有的风险, 并且会断定其得出的结果是不是与相等。$ pytest test session starts platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y rootdir: /home/sweet/project collected 3 items test_expectation.py ..F [100%] FAILURES ____________________________ test_eval[6*9-42] _____________________________ test_input 6*9, expected 42 pytest.mark.parametrize(test_input,expected, [(35, 8), (24, 6), (6*9, 42)]) def test_eval(test_input, expected): assert eval(test_input) expected E AssertionError: assert 54 42 E where 54 eval(6*9) test_expectation.py:6: AssertionError short test summary info FAILED test_expectation.py::test_eval[6*9-42] - AssertionError: assert 54... 1 failed, 2 passed in 0.12s 注意参数值会原样传递给测试不进行任何复制。比如说, 要是你把一个列表或者字典当作参数值去传递, 而且测试用例代码对它做了修改, 那么这些修改会在后续的测试用例调用当中体现出来。注意凡是参数化里所运用的字符串内的任意非ASCII字符, 默认情形下都会予以转义, 只不过这般处置存在若干弊端, 然而, 要是诸位巴望于参数化里借由字符串来达成目的, 并且能够在终端当中直接目睹它们不实施转义操作, 务必要在诸位的.ini文件里采用以下选项:[pytest] disable_test_id_escaping_and_forfeit_all_rights_to_community_support True可是, 要记住, 这没准会按照你采用的操作系统以及当下安装的插件萌生不期望的副作用甚至酿出错讹, 所以请自行承担风险去使用。依据本例当中的设计, 仅有一对输入以及输出值, 没能顺利通过简单的测试函数。跟测试函数参数相同, 你能够在回溯信息里边, 看到输入值以及输出值。请注意, 你同样能够在类或者模块之上运用标记。参见“怎样使用属性标记测试函数”, 这般将会借助被设置的参数集合去调用很多个函数。比如说:import pytest pytest.mark.parametrize(n,expected, [(1, 2), (3, 4)]) class TestClass: def test_simple_case(self, n, expected): assert n 1 expected def test_weird_simple_case(self, n, expected): assert (n * 1) 1 expected该类采用了特定的装饰器去对测试方法给予参数化, 就产生如下情况, 即存在两个测试方法, 它们会针对由特定装饰器所提供的每一组参数值, 分别都执行一回。为了在模块里对所有测试进行参数化, 您能够把全局变量设定成一个具有.mark.标识的标记。import pytest # 将pytestmark全局变量设置为一个parametrize标记该标记会应用于模块中的所有测试 pytestmark pytest.mark.parametrize(n,expected, [(1, 2), (3, 4)]) class TestClass: def test_simple_case(self, n, expected): assert n 1 expected def test_weird_simple_case(self, n, expected): assert (n * 1) 1 expected经设置后, 当中存在两个测试方法, 分别是那个和se, 它们都会接收到源自.mark.的(n, )参数集, 并且针对每一组参数各自执行一回。然而, 要留意, 要是模块里还有别的不在类中的测试函数, 它们也会收到这些参数, 除非它们明确地不接纳这些额外的参数。此外, 这种方法能够发挥作用, 然而它有可能致使测试的组织与意图, 变得不那么明晰, 尤其是在模块当中涵盖多个测试类的情形下。于其中, 您同样能够在进行参数化之际, 将测试实例予以单独标记, 像采用内置的 mark.xfail 去标记部分预计会失败的测试用例。# test_expectation.py 文件的内容 import pytest # 使用 pytest.mark.parametrize 来参数化测试同时标记特定的测试实例 pytest.mark.parametrize( test_input,expected, # 参数化测试的输入和期望输出 [ (35, 8), # 第一个测试实例预期通过 (24, 6), # 第二个测试实例预期通过 # 第三个测试实例预期失败使用 pytest.param 来明确指定参数并通过 marks 参数添加 xfail 标记 pytest.param(6*9, 42, markspytest.mark.xfail), ], ) def test_eval(test_input, expected): # 使用 eval 函数计算 test_input 的结果并断言该结果是否等于 expected assert eval(test_input) expected函数, 被参数化, 成为三个测试实例, 前两个实例, 分别是35, 8 和24, 6, 预期会, 成功通过, 因为 eval(35) 确实等于 8eval(24) 确实等于 6。第三个实例, 是6*9, 42, 使用了 .param 来明确指定参数, 并通过 marks.mark.xfail 将其标记为预期失败xfail。这代表着即便是这个测试用例出现了失败的情况, 也会把它当作是“预期失败” , 而非是真正的发生了失败, 进而不会对测试的总体结果产生影响除非你进行了相应配置 来严格地对待xfail。采用.param以及marks参数, 为参数化测试里的特定实例增添标记, 这是一种具备灵活性的方式用以把控测试的预期行为, 尤其是在你意欲清晰表明某些测试用例属于已知问题或者是尚未达成的特性之际。让我们运行一下:$ pytest test session starts platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y rootdir: /home/sweet/project collected 3 items test_expectation.py ..x [100%] 2 passed, 1 xfailed in 0.12s 之前导致失败的参数集现在显示为“”预期会失败测试。倘若所提供给的那值致使出现了一个空的列表, 举例来说, 要是它们是经由某个函数动态生成而成的, 那么, 所涉及的那行为将要由选项予以定义。为了获取多个参数化参数的所有组合你可以堆叠 装饰器import pytest pytest.mark.parametrize(x, [0, 1]) pytest.mark.parametrize(y, [2, 3]) def test_foo(x, y): pass这会运行测试, 把参数设定为x等于0、y等于2, x等于1、y等于2, x等于0、y等于3以及x等于1、y等于3, 按照装饰器的顺序用尽参数。基础的 s 示例有的时候, 您或许会期望达成自身的参数化规划, 又或者是为了给确定夹具的参数亦或是作用域达成某些动态特性。针对这个情况, 您能够于收集测试函数之际运用 s 钩子。凭借传入的 对象, 您能够查验请求的测试上下文, 最为关键的是, 您能够调用.() 以此来达成参数化。例如, 假定我们打算开展一项测试, 这项测试接纳字符串输入, 而我们期望借助一个全新的命令行选项去设定这些输入。首先, 我们着手编写一个简易的测试, 这个测试接纳一个名为的夹具函数参数:# content of test_strings.py def test_valid_string(stringinput): assert stringinput.isalpha() #isalpha() 是 Python 中的一个字符串方法用于检查字符串是否只包含字母现在, 我们置入一个.py文件, 此文件涵盖增添命令行选项以及测试函数的参数化。# content of conftest.py def pytest_addoption(parser): parser.addoption( --stringinput, actionappend, default[], help传递给测试函数的字符串输入列表, ) def pytest_generate_tests(metafunc): if stringinput in metafunc.fixturenames: metafunc.parametrize(stringinput, metafunc.config.getoption(stringinput))在这个 .py 文件中我们做了以下事情借助钩子添加了一个名为 -- 的命令行选项, 该选项采用 的形式, 进而意味着用户能够在命令行里多次指定此选项, 而且所有值都会被收纳到一个列表中, 表明要是没有提供 -- 选项, 那么默认值为一个空列表。借助s钩子, 依据--选项给出的值, 对测试函数开展参数化。在此示例里, 我们假定用户期望把各个字符串输入当作独立的测试情形。所以, 我们通过对列表予以遍历此列表是经由..()取得的, 并针对每个输入生成一个测试情形, 以此达成这一目的。如果现在传入两个值测试将运行两次:$ pytest -q --stringinputhello --stringinputworld test_strings.py .. [100%] 2 passed in 0.12s让我们再运行一个它将导致测试失败:$ pytest -q --stringinput! test_strings.py F [100%] FAILURES ___________________________ test_valid_string[!] ___________________________ stringinput ! def test_valid_string(stringinput): assert stringinput.isalpha() E AssertionError: assert False E where False () E where !.isalpha test_strings.py:4: AssertionError short test summary info FAILED test_strings.py::test_valid_string[!] - AssertionError: assert False 1 failed in 0.12s正如所料我们的测试函数失败了。要是你并未指定, 那它会被跳过, 鉴于, 给调用时将会返回一个空的参数列表, :。$ pytest -q -rs test_strings.py s [100%] short test summary info SKIPPED [1] test_strings.py: got empty parameter set [stringinput], function test_valid_string at /home/sweet/project/test_strings.py:2 1 skipped in 0.12s要留意, 在借由不同的参数集多次去进行调用.的时候, 那些集合里全部的参数名称均不可以出现重复的情况, 不然的话就会引发错误。注有一个钩子函数, 其中存在一个属于某类的实例, 有一种事物是标准库里的一个能用于编写用户友好命令行接口的模块, 在该事物的上下文环境中, 钩子可让你往事物的命令行接口添加自定义的一些选项。对象能够提供好些种用来定义这些选项的方法, 其中涵盖了( )以及( ) , 这里面的后者实际上是专门特有的一种包装器, 是被用来将给 添加与 存在联系的选项的这个过程予以简化的。可是呢, 在 钩子这个范畴当中, 一般情况下会直接去运用 .() 这种方法原因在于它已然针对 做好了适宜的配置。这个方法, 能让你去定义一个全新的命令行选项, 涵盖其名称, 类型, 是不是必须得要提供值, 默认值, 帮助信息等等。此方法会自动把这些选项增添到的命令行接口里, 而且你能够在测试期间经由的配置对象去访问这些选项的值。存在一种途径, 可借此去获取经由命令行接口也就是 CLI 传递进来的自定义选项之值。此处所提及的它, 是一个特别的对象。这个对象在 s 钩子函数当中是可用的, 其作用在于对测试函数的参数化加以控制。然而, 有一点要留意的是, 在绝大多数的测试函数里面, 你并非会直接去使用它。而是有可能会借助 对象来获取配置选项。但在参数化测试或者特定的钩子函数里面, 它能够提供对测试函数元数据的访问, 这里面包含了配置选项。..() 的工作方式如下