我正在使用py.test测试一些 Package 在python类MyTester中的DLL代码。出于验证的目的,我需要在测试过程中记录一些测试数据,并在之后做更多的处理。由于我有很多test_...文件,我想在我的大多数测试中重用测试器对象创建(MyTester的示例)。
由于测试器对象是获取DLL的变量和函数的引用的对象,因此我需要将DLL的变量列表传递给每个测试文件的测试器对象(要记录的变量与test_...文件的相同)。列表的内容用于记录指定的数据。
我的想法是这样做:
import pytest
class MyTester():
def __init__(self, arg = ["var0", "var1"]):
self.arg = arg
# self.use_arg_to_init_logging_part()
def dothis(self):
print "this"
def dothat(self):
print "that"
# located in conftest.py (because other test will reuse it)
@pytest.fixture()
def tester(request):
""" create tester object """
# how to use the list below for arg?
_tester = MyTester()
return _tester
# located in test_...py
# @pytest.mark.usefixtures("tester")
class TestIt():
# def __init__(self):
# self.args_for_tester = ["var1", "var2"]
# # how to pass this list to the tester fixture?
def test_tc1(self, tester):
tester.dothis()
assert 0 # for demo purpose
def test_tc2(self, tester):
tester.dothat()
assert 0 # for demo purpose
有没有可能做到这一点,甚至有一个更优雅的方式?
通常我可以用某种设置函数(xUnit风格)为每个测试方法做这件事。但是我想获得某种重用。有人知道这对fixture是否可能吗?
我知道我可以这样做:(来自文档)
@pytest.fixture(scope="module", params=["merlinux.eu", "mail.python.org"])
但是我需要直接在测试模块中进行参数化。是否可以从测试模块访问夹具的params属性?
9条答案
按热度按时间wydwbb8l1#
这实际上是通过间接参数化在py.test中原生支持的。
在您的情况下,您将:
llew8vvj2#
**更新:**因为这是这个问题的公认答案,而且有时候仍然会得到支持,我应该添加一个更新。虽然我最初的答案(下面)是在旧版本的pytest中实现这一点的唯一方法,因为others有noted pytest现在支持fixture的间接参数化。例如,你可以这样做(通过@imiric):
第一个
然而,尽管这种间接参数化形式是显式的,如@Yukihiko Shinoda points out,它现在支持一种隐式间接参数化形式(尽管我在官方文档中找不到任何明显的引用):
第一次
我不知道这种形式的确切语义是什么,但
pytest.mark.parametrize
似乎认识到,尽管test_tc1
方法没有接受名为tester_arg
的参数,但它所使用的tester
fixture接受,因此它通过tester
fixture传递参数化的参数。我也遇到过类似的问题--我有一个名为
test_package
的fixture,后来我希望在特定测试中运行它时能够向该fixture传递一个可选参数。(It fixture做什么或者返回的
package
)是什么类型的对象并不重要。然后,希望以某种方式在测试函数中使用这个fixture,这样我也可以指定
version
参数给那个fixture,以便在测试中使用。这目前是不可能的,尽管可能会成为一个很好的特性。与此同时,让我的fixture返回一个 function 非常简单,它完成fixture以前完成的所有工作,但允许我指定
version
参数:现在,我可以在测试函数中使用它,例如:
和/或其他信息。
OP尝试的解决方案方向是正确的,正如@hpk42的answer所建议的,
MyTester.__init__
可以存储对请求的引用,如下所示:然后使用此方法实现夹具,如下所示:
如果需要的话,
MyTester
类可以重新构造一点,这样它的.args
属性可以在创建之后更新,从而调整各个测试的行为。nkkqxpd93#
但是我找不到任何文件
正如@chilicheech指出的那样,
至少从pytest 6.2(2020年12月13日发布)开始,官方记录了这种方式:
它似乎在最新版本的pytest中工作。
ntjbwcob4#
你也可以使用闭包,这将给予你更全面的命名和对参数的控制。它们比 * 隐式参数化 * 中使用的
request
参数更“显式”:我用它来构建可配置的设备
pu82cl6c5#
你可以从fixture函数(也就是你的Tester类)访问请求的模块/类/函数,参见从fixture函数与请求的测试上下文交互。所以你可以在类或模块上声明一些参数,测试器fixture可以拾取它。
okxuctiv6#
imiric's answer稍微改进一下:解决这个问题的另一个优雅的方法是创建“参数fixture”,我个人更喜欢它而不是
pytest
的indirect
特性,这个特性可以从pytest_cases
获得,最初的想法是由Sup3rGeo提出的。请注意,
pytest-cases
还提供了@fixture
,允许您直接在fixture上使用参数化标记,而不必使用@pytest.fixture(params=...)
和
@parametrize_with_cases
,它允许您从“case函数”中获取参数,这些函数可能被分组在一个类中,甚至是一个单独的模块中。)xj3cbfub7#
我做了一个有趣的装饰器,它允许编写如下的固定装置:
在这里,
/
的左边是其他fixture,右边是使用提供的参数:这和函数参数的工作方式是一样的。如果你没有提供
age
参数,默认的69
参数会被使用。如果你没有提供name
,或者省略了dog.arguments
装饰器,你会得到常规的TypeError: dog() missing 1 required positional argument: 'name'
。如果你有另一个fixture使用name
参数,它不会和这个fixture冲突。还支持异步装置。
此外,这还为您提供了一个很好的设置计划:
完整示例:
装饰器的代码:
pepwfjgg8#
另一种方法是使用request对象访问在定义测试函数的模块或类中定义的变量。
这样,如果您想为类/模块的所有测试函数传递相同的变量,就不必在测试类的每个函数上重用
@pytest.mark.parametrize()
装饰器。使用类变量的示例:
这样,test_tc1和test_tc2的
tester
对象将使用tester_args
参数进行初始化。您也可以用途:
request.function
访问函数test_tc1,request.instance
来访问TestIt类示例,request.module
访问模块TestIt在request
文档)sf6xfgos9#
另一种方法是使用自定义标记。它看起来比代码中的参数化更好,不反映在测试名称中,并且也是可选的(如果不存在这样的标记,可以通过引发失败将其定义为非可选的)
例如:
当我需要一个模拟ssh客户机的fixture,并且想要测试不同的可能输出时,我就使用这个标记来决定每个测试的输出。
请注意,如果是个人使用,则不需要测试失败的故障保存机制。