如何配置 pytest 在生成测试时生成有用的名称?
How can I configure pytest to generate useful names when generating tests?
我正在使用 py.test 执行一套 selenium 测试。我本质上是 运行 我 conftest.py 中的一个收集器,它生成这样的测试(我从 pytest 文档中偷了这个):
def pytest_generate_tests(metafunc):
funcarglist = metafunc.cls.cases[metafunc.function.__name__]
argnames = list(funcarglist[0])
metafunc.parametrize(argnames, [[funcargs[i] for i in argnames]
for funcargs in funcarglist])
我的测试用例放置在如下所示的对象中:
class TestObject(object):
def __init__(
self,
parameter_1,
):
self.parameter_1 = parameter_1
self.parameter_2 = parameter_2
我像这样实例化它们:
test_cases_values = {
"friendly_case_name_1": TestObject(
"parameter_1_value",
"parameter_2_value"
),
"friendly_case_name_2": TestObject(
"parameter_1_value",
"parameter_2_value"
),
}
我的浏览器连接到一个网格服务器,我把它们列成这样:
BROWSERS = [
"('browser_1', SERVER_URL)",
"('browser_2', SERVER_URL)"
]
我将目标环境存储在一个配置文件中,该文件是这样一个对象的实例化:
class Environment(object):
def __init__(self, url=URL, port=PORT):
self.url = url
self.port = port
def __name__(self):
return self.url + ":" + self.port
ENVIRONMENT = Environment()
然后我有一个测试 class,它创建了一个这样的测试用例列表 - 测试对象参数实际上是允许自生成代码的字符串。当我将它们作为填充物传递给更广泛的 exec 语句时,我过于简单化了:
class TestClass(object):
cases = {"test_function": []}
for i in test_cases.values():
for j in BROWSERS:
cases["test_function"].append(
dict(
browser=j,
environment=ENVIRONMENT
test_object=i
)
)
@pytest.mark.run()
def test_function(
self,
browser,
environment,
test_object
):
exec(test_object.parameter_1)
exec(test_object.parameter_2)
assert my_assertion
当收集器运行时,它看起来像这样:
collected # items
<Module 'tests.py'>
<Class 'TestClass'>
<Instance '()'>
<Function "test_function[environment0-test_object0-('browser_1', GRID_SERVER)]">
<Function "test_function[environment1-test_object1-('browser_2', GRID_SERVER)]">
<Function "test_function[environment2-test_object2-('browser_1', GRID_SERVER)]">
<Function "test_function[environment3-test_object3-('browser_2', GRID_SERVER)]">
我想让收集器以这样一种方式工作,即我可以返回有关每个项目的有用信息 - 我搞砸了设置 __str__、__repr__ 和 __name__ 方法在各个地方,但没有得到我预期的结果。我希望能够将其纳入报告 - 这在生产中生成了 200 多个测试,我必须跟踪当前的堆栈跟踪以找出每次失败的确切测试内容。
我不太确定我在哪里犯了错误,我应该修改 pytest_generate_tests 的实现,还是我创建 TestClass 的方式,或者以不同的方式设置案例方法?理想情况下,我想要一些可以通过 ORM 映射回来的东西,以包括测试元数据。
好吧,我明白了。结果是 metafunc.parametrize 函数接受 "ids" 作为参数。我所要做的就是定义我要命名的对象的 __repr__,并扩展列表理解,这样我就可以 return 来自同一个循环的两个东西。
def pytest_generate_tests(metafunc):
funcarglist = metafunc.cls.cases[metafunc.function.__name__]
argnames = list(funcarglist[0])
argvalues = []
ids = []
for i in funcarglist:
inner_argvalues_list = []
inner_ids_list = []
for j in argnames:
inner_argvalues_list.append(i[j])
if type(i[j]) != str:
inner_ids_list.append(i[j].__repr__())
else:
inner_ids_list.append(i[j])
argvalues.append(inner_argvalues_list)
ids.append(inner_ids_list)
metafunc.parametrize(argnames, argvalues, ids=ids)
我正在使用 py.test 执行一套 selenium 测试。我本质上是 运行 我 conftest.py 中的一个收集器,它生成这样的测试(我从 pytest 文档中偷了这个):
def pytest_generate_tests(metafunc):
funcarglist = metafunc.cls.cases[metafunc.function.__name__]
argnames = list(funcarglist[0])
metafunc.parametrize(argnames, [[funcargs[i] for i in argnames]
for funcargs in funcarglist])
我的测试用例放置在如下所示的对象中:
class TestObject(object):
def __init__(
self,
parameter_1,
):
self.parameter_1 = parameter_1
self.parameter_2 = parameter_2
我像这样实例化它们:
test_cases_values = {
"friendly_case_name_1": TestObject(
"parameter_1_value",
"parameter_2_value"
),
"friendly_case_name_2": TestObject(
"parameter_1_value",
"parameter_2_value"
),
}
我的浏览器连接到一个网格服务器,我把它们列成这样:
BROWSERS = [
"('browser_1', SERVER_URL)",
"('browser_2', SERVER_URL)"
]
我将目标环境存储在一个配置文件中,该文件是这样一个对象的实例化:
class Environment(object):
def __init__(self, url=URL, port=PORT):
self.url = url
self.port = port
def __name__(self):
return self.url + ":" + self.port
ENVIRONMENT = Environment()
然后我有一个测试 class,它创建了一个这样的测试用例列表 - 测试对象参数实际上是允许自生成代码的字符串。当我将它们作为填充物传递给更广泛的 exec 语句时,我过于简单化了:
class TestClass(object):
cases = {"test_function": []}
for i in test_cases.values():
for j in BROWSERS:
cases["test_function"].append(
dict(
browser=j,
environment=ENVIRONMENT
test_object=i
)
)
@pytest.mark.run()
def test_function(
self,
browser,
environment,
test_object
):
exec(test_object.parameter_1)
exec(test_object.parameter_2)
assert my_assertion
当收集器运行时,它看起来像这样:
collected # items <Module 'tests.py'> <Class 'TestClass'> <Instance '()'> <Function "test_function[environment0-test_object0-('browser_1', GRID_SERVER)]"> <Function "test_function[environment1-test_object1-('browser_2', GRID_SERVER)]"> <Function "test_function[environment2-test_object2-('browser_1', GRID_SERVER)]"> <Function "test_function[environment3-test_object3-('browser_2', GRID_SERVER)]">
我想让收集器以这样一种方式工作,即我可以返回有关每个项目的有用信息 - 我搞砸了设置 __str__、__repr__ 和 __name__ 方法在各个地方,但没有得到我预期的结果。我希望能够将其纳入报告 - 这在生产中生成了 200 多个测试,我必须跟踪当前的堆栈跟踪以找出每次失败的确切测试内容。
我不太确定我在哪里犯了错误,我应该修改 pytest_generate_tests 的实现,还是我创建 TestClass 的方式,或者以不同的方式设置案例方法?理想情况下,我想要一些可以通过 ORM 映射回来的东西,以包括测试元数据。
好吧,我明白了。结果是 metafunc.parametrize 函数接受 "ids" 作为参数。我所要做的就是定义我要命名的对象的 __repr__,并扩展列表理解,这样我就可以 return 来自同一个循环的两个东西。
def pytest_generate_tests(metafunc):
funcarglist = metafunc.cls.cases[metafunc.function.__name__]
argnames = list(funcarglist[0])
argvalues = []
ids = []
for i in funcarglist:
inner_argvalues_list = []
inner_ids_list = []
for j in argnames:
inner_argvalues_list.append(i[j])
if type(i[j]) != str:
inner_ids_list.append(i[j].__repr__())
else:
inner_ids_list.append(i[j])
argvalues.append(inner_argvalues_list)
ids.append(inner_ids_list)
metafunc.parametrize(argnames, argvalues, ids=ids)