mag*_*gie 92 python fixtures pytest
我正在使用py.test来测试包含在python类MyTester中的一些DLL代码.为了验证目的,我需要在测试期间记录一些测试数据,然后再进行更多处理.由于我有很多测试_...文件,我想在大多数测试中重用测试器对象创建(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
Run Code Online (Sandbox Code Playgroud)
有可能像这样实现它还是有更优雅的方式?
通常我可以使用某种设置函数(xUnit-style)为每个测试方法执行此操作.但我希望获得某种重用.有没有人知道这是否可以使用灯具?
我知道我可以这样做:(来自文档)
@pytest.fixture(scope="module", params=["merlinux.eu", "mail.python.org"])
Run Code Online (Sandbox Code Playgroud)
但是我需要在测试模块中直接进行参数化. 是否可以从测试模块访问夹具的params属性?
imi*_*ric 128
这实际上是通过间接参数化在py.test中本地支持的.
在你的情况下,你会有:
@pytest.fixture
def tester(request):
"""Create tester object"""
return MyTester(request.param)
class TestIt:
@pytest.mark.parametrize('tester', [['var1', 'var2']], indirect=True)
def test_tc1(self, tester):
tester.dothis()
assert 1
Run Code Online (Sandbox Code Playgroud)
Igu*_*aut 87
我有一个类似的问题 - 我有一个夹具调用test_package,后来我想在特定的测试中运行它时能够将一个可选的参数传递给该夹具.例如:
@pytest.fixture()
def test_package(request, version='1.0'):
...
request.addfinalizer(fin)
...
return package
Run Code Online (Sandbox Code Playgroud)
(对于这些目的而言,夹具的功能或返回的对象类型无关紧要package).
然后,需要以某种方式在测试函数中使用此夹具,以便我还可以指定该version夹具的参数以与该测试一起使用.目前这是不可能的,但可能会成为一个很好的功能.
与此同时,使我的夹具简单地返回一个能完成夹具先前完成的所有工作的功能,这很容易,但允许我指定version参数:
@pytest.fixture()
def test_package(request):
def make_test_package(version='1.0'):
...
request.addfinalizer(fin)
...
return test_package
return make_test_package
Run Code Online (Sandbox Code Playgroud)
现在我可以在我的测试函数中使用它,如:
def test_install_package(test_package):
package = test_package(version='1.1')
...
assert ...
Run Code Online (Sandbox Code Playgroud)
等等.
OP的尝试解决方案正朝着正确的方向前进,正如@ hpk42的回答所暗示的那样,MyTester.__init__可以存储对请求的引用,如:
class MyTester(object):
def __init__(self, request, arg=["var0", "var1"]):
self.request = request
self.arg = arg
# self.use_arg_to_init_logging_part()
def dothis(self):
print "this"
def dothat(self):
print "that"
Run Code Online (Sandbox Code Playgroud)
然后使用它来实现夹具,如:
@pytest.fixture()
def tester(request):
""" create tester object """
# how to use the list below for arg?
_tester = MyTester(request)
return _tester
Run Code Online (Sandbox Code Playgroud)
如果需要,MyTester可以对类进行一些重构,以便.args在创建它之后更新其属性,以调整各个测试的行为.
din*_*igo 21
您还可以使用闭包,这将为您提供更全面的命名和对参数的控制。它们比隐式参数化request中使用的参数更“显式” :
@pytest.fixture
def tester():
# Create a closure on the Tester object
def _tester(first_param, second_param):
# use the above params to mock and instantiate things
return MyTester(first_param, second_param)
# Pass this closure to the test
yield _tester
@pytest.mark.parametrize(['param_one', 'param_two'], [(1,2), (1000,2000)])
def test_tc1(tester, param_one, param_two):
# run the closure now with the desired params
my_tester = tester(param_one, param_two)
# assert code here
Run Code Online (Sandbox Code Playgroud)
我用它来构建可配置的装置。
Yuk*_*oda 19
我找不到任何文件,但是,它似乎在最新版本的 pytest 中工作。
@pytest.fixture
def tester(tester_arg):
"""Create tester object"""
return MyTester(tester_arg)
class TestIt:
@pytest.mark.parametrize('tester_arg', [['var1', 'var2']])
def test_tc1(self, tester):
tester.dothis()
assert 1
Run Code Online (Sandbox Code Playgroud)
hpk*_*k42 11
您可以从fixture函数(以及Tester类)访问请求模块/类/函数,查看与fixture函数请求测试上下文的交互.因此,您可以在类或模块上声明一些参数,并且测试仪夹具可以拾取它.
改进一点imiric 的答案:解决此问题的另一种优雅方法是创建“参数装置”。我个人更喜欢它在indirect的功能pytest。此功能可从 获得pytest_cases,最初的想法是由Sup3rGeo提出的。
import pytest
from pytest_cases import param_fixture
# create a single parameter fixture
var = param_fixture("var", [['var1', 'var2']], ids=str)
@pytest.fixture
def tester(var):
"""Create tester object"""
return MyTester(var)
class TestIt:
def test_tc1(self, tester):
tester.dothis()
assert 1
Run Code Online (Sandbox Code Playgroud)
请注意,pytest-cases还提供@fixture允许您直接在灯具上使用参数化标记,而不必使用@pytest.fixture(params=...)
from pytest_cases import fixture, parametrize
@fixture
@parametrize("var", [['var1', 'var2']], ids=str)
def tester(var):
"""Create tester object"""
return MyTester(var)
Run Code Online (Sandbox Code Playgroud)
并且@parametrize_with_cases,可以让你从可能的类,甚至一个单独的模块进行分组“功能的情况下,” SOURCE您的参数。有关详细信息,请参阅文档。顺便说一下,我是作者;)
我做了一个有趣的装饰器,它允许编写这样的装置:
@fixture_taking_arguments
def dog(request, /, name, age=69):
return f"{name} the dog aged {age}"
Run Code Online (Sandbox Code Playgroud)
在这里,左侧/有其他灯具,右侧有使用以下方法提供的参数:
@dog.arguments("Buddy", age=7)
def test_with_dog(dog):
assert dog == "Buddy the dog aged 7"
Run Code Online (Sandbox Code Playgroud)
这与函数参数的工作方式相同。如果您不提供age参数,则使用默认参数 , 69。如果您不提供name或省略dog.arguments装饰器,您将获得常规的TypeError: dog() missing 1 required positional argument: 'name'. 如果你有另一个需要参数的装置name,它与这个没有冲突。
还支持异步装置。
此外,这为您提供了一个不错的设置计划:
$ pytest test_dogs_and_owners.py --setup-plan
SETUP F dog['Buddy', age=7]
...
SETUP F dog['Champion']
SETUP F owner (fixtures used: dog)['John Travolta']
Run Code Online (Sandbox Code Playgroud)
一个完整的例子:
@dog.arguments("Buddy", age=7)
def test_with_dog(dog):
assert dog == "Buddy the dog aged 7"
Run Code Online (Sandbox Code Playgroud)
装饰器的代码:
$ pytest test_dogs_and_owners.py --setup-plan
SETUP F dog['Buddy', age=7]
...
SETUP F dog['Champion']
SETUP F owner (fixtures used: dog)['John Travolta']
Run Code Online (Sandbox Code Playgroud)
另一种方法是使用请求对象来访问定义测试函数的模块或类中定义的变量。
@pytest.mark.parametrize()这样,如果您想为类/模块的所有测试函数传递相同的变量,则不必在测试类的每个函数上重用装饰器。
类变量的示例:
@pytest.fixture
def tester(request):
"""Create tester object"""
return MyTester(request.cls.tester_args)
class TestIt:
tester_args = ['var1', 'var2']
def test_tc1(self, tester):
tester.dothis()
def test_tc2(self, tester):
tester.dothat()
Run Code Online (Sandbox Code Playgroud)
这样testertest_tc1 和 test_tc2 的对象都将使用tester_args参数进行初始化。
您还可以使用:
request.function访问 test_tc1 函数,request.instance访问 TestIt 类实例,request.module访问模块 TestIt 的定义request文档)小智 5
另一种方法是使用自定义标记。它看起来比代码中的参数化更好,没有反映在测试名称中,并且也是可选的(如果不存在这样的标记,可以通过引发失败来定义为不可选)
例如:
@pytest.fixture
def loaded_dll(request):
dll_file = None
for mark in request.node.iter_markers("dll_file"):
if mark.args:
if dll_file is not None:
pytest.fail("Only one dll_file can be mentioned in marks")
dll_file = mark.args[0]
if dll_file is None:
pytest.fail("dll_file is a required mark")
return some_dll_load(dll_file)
@pytest.mark.dll_file("this.dll")
def test_this_dll(loaded_dll):
...
Run Code Online (Sandbox Code Playgroud)
当我需要一个模拟 ssh 客户端的装置并想要测试不同的可能输出时,我在测试中使用了它,我可以使用标记来决定每个测试的输出。
请注意,如果是供个人使用,则不需要未通过测试的故障保存机制。