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.

Files for pytest-paramark, version 0.1.1
Filename, size File type Python version Upload date Hashes
Filename, size pytest-paramark-0.1.1.linux-x86_64.tar.gz (5.6 kB) File type Source Python version None Upload date Hashes View hashes
Filename, size pytest_paramark-0.1.1-py3-none-any.whl (5.4 kB) File type Wheel Python version py3 Upload date Hashes View hashes

Supported by

Elastic Elastic Search Pingdom Pingdom Monitoring Google Google BigQuery Sentry Sentry Error logging AWS AWS Cloud computing DataDog DataDog Monitoring Fastly Fastly CDN DigiCert DigiCert EV certificate StatusPage StatusPage Status page