Skip to main content

Configure pytest fixtures using a combination of"parametrize" and markers

Project description

PyPI version Python versions See Build Status on Travis CI

Configure pytest fixtures using combination of parametrize and markers


What is this thing?

The problem

Pytest fixture names must be unique within the whole dependency graph (#3966).

This means that when you want to parametrize fixtures, each parameter name must be unique:

import pytest

@pytest.fixture
def foo(foo_option):
   return {'option': foo_option}


@pytest.fixture
def bar(bar_option):
   return {'option': bar_option}


@pytest.mark.parametrize(
   'foo_option, bar_option',
   [
      (42, 24),
   ]
)
def test_options(foo, bar):
   assert foo['option'] == 42
   assert bar['option'] == 24

Also, if you want to provide default vaules for options, they need to be fixtures as well:

@pytest.fixture
def foo_option():
   return 'default_foo_option'


@pytest.fixture
def bar_option():
   return 'default_bar_option'


@pytest.fixture
def foo(foo_option):
   return {'option': foo_option}


@pytest.fixture
def bar(bar_option):
   return {'option': bar_option}


def test_options(foo, bar):
   assert foo['option'] == 'default_foo_option'
   assert bar['option'] == 'default_bar_option'

This is inconvenient when number of options and fixtures increases, and you end up with lots of boilerplate code like this:

@pytest.fixture()
def app_elements():
   {}


@pytest.fixture()
def app_sequence():
   return None


@pytest.fixture()
def app_uuid(uuid=None):
   return uuid or uuid4()


@pytest.fixture
def app_app_key():
   return ApplicationKey(bytes.fromhex('63964771734fbd76e3b40519d1d94a48'))


@pytest.fixture
def app_net_key():
   return NetworkKey(bytes.fromhex('7dd7364cd842ad18c17c2b820c84c3d6'))


@pytest.fixture
def app_dev_key():
   return DeviceKey(bytes.fromhex('9d6dd0e96eb25dc19a40ed9914f8f03f'))


@pytest.fixture
def app_addr():
   return 0x5f2


@pytest.fixture
def app_iv_index():
   return 0


@pytest.fixture()
def application(app_uuid, app_elements, app_dev_key, app_app_key, app_net_ket,
                app_addr, app_iv_index, app_sequence):
   ...

The solution

This plugin provides a cleaner way to pass such options to selected fixutres, by implementing a magic fixture called paramark, which returns a different value for each of the fixtures that depend on it:

@pytest.fixture
def foo(paramark):
   return paramark


@pytest.fixture
def bar(paramark):
   return paramark


@pytest.mark.foo(option=42)
@pytest.mark.bar(option=24)
def test_options(foo, bar):
   assert foo['option'] == 42
   assert bar['option'] == 24

As can be seen in the example, paramark returns a dictionary with keys and values pulled from a custom mark with the same name as the dependant fixture. Note that these marks still need to be registered.

This also works with parametrize, by extending the argument name syntax to include a dot:

@pytest.mark.parametrize(
   'foo.option, bar.option',
   [
      (43, 24),
   ]
)
@pytest.mark.bar(option=24)
def test_options(foo, bar):
   assert foo['option'] == 42
   assert bar['option'] == 24

or, if you want to parametrize the whole dictionary:

@pytest.mark.parametrize(
   'foo.*, bar.option',
   [
      ({'option': 42, 'another: 17}, 24),
   ]
)
@pytest.mark.bar(option=24)
def test_options(foo, bar):
   assert foo['option'] == 42
   assert foo['another'] == 17
   assert bar['option'] == 24

Having this, defining default values no longer requires separate fixture for each option:

@pytest.fixture
def foo(paramark):
   default = {'option': 'default_foo_option'}
   return {**default, **paramark)


@pytest.fixture
def bar(paramark):
   default = {'option': 'default_bar_option'}
   return {**default, **paramark)


@pytest.mark.foo(option='custom_foo_option')
def test_options(foo, bar):
   assert foo['option'] == 'custom_foo_option'
   assert bar['option'] == 'default_bar_option'

or, if you want to be safer and fancier:

import typing


@pytest.fixture
def foo(paramark):
   class Foo(typing.NamedTuple):
      option: str = 'default_foo_option'

   return Foo(**paramark)


def test_options(foo):
   assert foo.option == 'default_foo_option'

Installation

You can install “pytest-paramark” via pip from PyPI:

$ pip install pytest-paramark

Contributing

Contributions are very welcome. Tests can be run with tox, please ensure the coverage at least stays the same before you submit a pull request.

License

Distributed under the terms of the MIT license, “pytest-paramark” is free and open source software

Issues

If you encounter any problems, please file an issue along with a detailed description.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pytest-paramark-0.1.1.linux-x86_64.tar.gz (5.6 kB view hashes)

Uploaded Source

Built Distribution

pytest_paramark-0.1.1-py3-none-any.whl (5.4 kB view hashes)

Uploaded Python 3

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page