I'm using py.test to execute a suite of selenium tests. I'm essentially running a collector in my conftest.py that generates tests like this (I stole this from the pytest documentation):
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])
My test cases are placed in objects that look like this:
class TestObject(object):
def __init__(
self,
parameter_1,
):
self.parameter_1 = parameter_1
self.parameter_2 = parameter_2
I instantiate them something like this:
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"
),
}
My browsers are attached to a grid server, I make a list of them like this:
BROWSERS = [
"('browser_1', SERVER_URL)",
"('browser_2', SERVER_URL)"
]
I store the target environment in a config file that is an instantiation of an object like this:
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()
Then I have a test class that creates a list of test cases like this - the test object parameters are actually strings that allow for self generating code. I'm oversimplifying as I pass them in as fill ins to broader exec statements:
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
When the collector runs, it looks like this:
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)]">
I want to have the collector work in such a way that I get back useful information about each item - I've messed around with setting __str__, __repr__, and __name__ methods in various places but haven't had the results I expected. I'd like to be able to roll this into reporting - there are over 200 tests that this generates in production and I have to trace through stack traces currently to figure out exactly what was being tested for each failure.
I'm not really sure where I'm making my mistakes here, should I modify my implementation of pytest_generate_tests, or the way I'm creating my TestClass, or set up the cases in a different way? Ideally, I want something that can be mapped back via an ORM to include test metadata as well.
Well, I figured it out. Turns out that the metafunc.parametrize function accepts "ids" as a parameter. All I had to do was define the __repr__ of the objects I was looking to name, and expanded the list comprehension so I could return two things from the same loop.
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)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With