Skip to content

Instantly share code, notes, and snippets.

@Lucretiel
Last active June 22, 2018 04:01
Show Gist options
  • Save Lucretiel/952eddbe2d343fdcf38cf95e8e146db9 to your computer and use it in GitHub Desktop.
Save Lucretiel/952eddbe2d343fdcf38cf95e8e146db9 to your computer and use it in GitHub Desktop.
Call a pytest test once for each value in a fixture list
import itertools
import functools
def keyed_product(**iterables):
'''
Same as itertools.product, but returns key-value dicts. Basically, given
a dict of key => list, return every possible combination of all the
values in the lists, and their associated keys.
Example:
keyed_product(x=[1,2], y=[3,4]) yields: [
{x: 1, y: 3},
{x: 1, y: 4},
{x: 2, y: 3},
{x: 2, y: 4},
]
'''
ordered_keys = tuple(iterables.keys())
combinations = itertools.product(*(iterables[key]) for key in ordered_keys)
return (dict(zip(ordered_keys, product)) for product in combinations)
def spread_fixtures(*fixture_names):
'''
For a given set of fixture names, call the decorated test function once
per each combination of those fixtures. The fixtures should be lists or
tuples. The test cases are called in a simple for loop.
'''
fixture_names = tuple(fixture_names)
def spread_fixtures_decorator(test_func):
@functools.wraps(test_func)
def spread_fixtures_wrapper(**kwargs):
# Check that the fixtures we're spreading are collections.
for fixture_name in fixture_names:
fixture_list = kwargs[fixture_name]
# For now we just require lists or tuples. Can grow later.
if not isinstance(fixture_list, (list, tuple)):
raise RuntimeError(
"Fixture '{}' supplied {!r} instead of a list or tuple"
.format(fixture_name, fixture_list))
# Loop over every possible combination of fixtures
for combo_kwargs in keyed_product(**{name: kwargs[name] for name in fixture_names}):
try:
test_func(**kwargs, **combo_kwargs)
except:
print("Combination failed: {}".format(combo_kwargs))
raise
return spread_fixtures_wrapper
return spread_fixtures_decorator
# EXAMPLE
@pytest.fixture
def aws():
return AWS()
@pytest.fixture
def cname(aws):
return aws.get_cnames() # this should return a list
@spread_fixtures('cname')
def test(aws, cname):
pass
# spread_cnames will cause the test function to be called once for each
# cname, with a simple for loop.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment