Metadata-Version: 2.4
Name: pytest-lazy-fixtures
Version: 1.4.0
Summary: Allows you to use fixtures in @pytest.mark.parametrize.
Project-URL: Homepage, https://github.com/dev-petrov/pytest-lazy-fixtures
Project-URL: Repository, https://github.com/dev-petrov/pytest-lazy-fixtures
Author-email: Petrov Anton <antonp2@yandex.ru>
License-Expression: MIT
License-File: LICENSE
Keywords: fixture,lazy,pytest,tests
Requires-Python: >=3.8
Requires-Dist: pytest>=7
Description-Content-Type: text/markdown

# pytest-lazy-fixtures

[![codecov](https://codecov.io/gh/dev-petrov/pytest-lazy-fixtures/branch/master/graph/badge.svg)](https://codecov.io/gh/dev-petrov/pytest-lazy-fixtures)
[![CI](https://github.com/dev-petrov/pytest-lazy-fixtures/workflows/CI/badge.svg)](https://github.com/dev-petrov/pytest-lazy-fixtures/actions/workflows/ci-test.yml)
[![PyPI version](https://badge.fury.io/py/pytest-lazy-fixtures.svg)](https://pypi.org/project/pytest-lazy-fixtures/)
[![PyPI downloads](https://img.shields.io/pypi/dm/pytest-lazy-fixtures)](https://pypistats.org/packages/pytest-lazy-fixtures)

Use your fixtures in `@pytest.mark.parametrize`.

This project was inspired by [pytest-lazy-fixture](https://github.com/TvoroG/pytest-lazy-fixture).

Improvements that have been made in this project:

1. You can use fixtures in any data structures
2. You can access the attributes of fixtures
3. You can use functions in fixtures
4. It is compatible with [pytest-deadfixtures](https://github.com/jllorencetti/pytest-deadfixtures)

## Installation

```shell
pip install pytest-lazy-fixtures
```

## Usage

To use your fixtures inside `@pytest.mark.parametrize` you can use `lf` (`lazy_fixture`).

```python
import pytest
from pytest_lazy_fixtures import lf

@pytest.fixture()
def one():
    return 1

@pytest.mark.parametrize('arg1,arg2', [('val1', lf('one'))])
def test_func(arg1, arg2):
    assert arg2 == 1
```

`lf` can be used with any data structures. For example, in the following example, `lf` is used in the dictionary:

```python
import pytest
from pytest_lazy_fixtures import lf

@pytest.fixture()
def one():
    return 1

@pytest.mark.parametrize('arg1,arg2', [('val1', {"value": lf('one')})])
def test_func(arg1, arg2):
    assert arg2 == {"value": 1}
```

You can also specify getting an attribute through a dot:

```python
import pytest
from pytest_lazy_fixtures import lf

class Entity:
    def __init__(self, value):
        self.value = value

@pytest.fixture()
def one():
    return Entity(1)

@pytest.mark.parametrize('arg1,arg2', [('val1', lf('one.value'))])
def test_func(arg1, arg2):
    assert arg2 == 1
```

And there is some useful wrapper called `lfc` (`lazy_fixture_callable`).
It can work with any callable and your fixtures.

```python
import pytest
from pytest_lazy_fixtures import lf, lfc

class Entity:
    def __init__(self, value):
        self.value = value

    def __str__(self) -> str:
        return str(self.value)

    def sum(self, value: int) -> int:
        return self.value + value

@pytest.fixture()
def entity():
    return Entity(1)

@pytest.fixture()
def two():
    return 2

@pytest.fixture()
def three():
    return 3

@pytest.fixture()
def entity_format():
    def _entity_format(entity: Entity):
        return {"value": entity.value}

    return _entity_format

# Recommended: implicit injection from the callable's parameter names
@pytest.mark.parametrize("as_text", [lfc(lambda entity: str(entity))])
def test_lfc_injection_basic(as_text):
    assert as_text == "1"

# Advanced: manual control over what to inject (explicit values into a callable)
@pytest.mark.parametrize(
    "message",
    [
        lfc(
            "There is two lazy fixture args, {} and {}! And one kwarg {field}! And also simple value {simple}".format,
            lf("entity"),
            lf("two"),
            field=lf("three"),
            simple="value",
        ),
    ],
)
def test_lazy_fixture_callable_with_func(message):
    assert message == "There is two lazy fixture args, 1 and 2! And one kwarg 3! And also simple value value"


@pytest.mark.parametrize("formatted", [lfc("entity_format", lf("entity"))])
def test_lazy_fixture_callable_with_lf(formatted, entity):
    assert formatted == {"value": entity.value}


@pytest.mark.parametrize("result", [lfc("entity.sum", lf("two"))])
def test_lazy_fixture_callable_with_attr_lf(result):
    assert result == 3
```

### Injecting fixtures from a callable signature

`lfc` automatically injects fixtures based on the callable's parameter names. If a parameter name matches a fixture name, that fixture will be resolved and passed into the callable. This implicit injection is the standard, recommended behavior. You can still override any parameter explicitly using `lf`, either positionally or by name.
Default values prevent implicit injection for that parameter.

Examples:

```python
import pytest
from pytest_lazy_fixtures import lf, lfc

# Some fixtures used in the examples
@pytest.fixture()
def alpha():
    return 10

@pytest.fixture()
def beta():
    return 20

@pytest.fixture()
def gamma():
    return 30

# Simple implicit injection by parameter name
@pytest.mark.parametrize("result", [lfc(lambda alpha: alpha + 1)])
def test_signature_injection_basic(result):
    assert result == 11

# Override an implicitly matched parameter (positional or keyword)
@pytest.mark.parametrize(
    "result",
    [
        lfc(lambda alpha: alpha + 1, lf("beta")),      # positional override
        lfc(lambda alpha: alpha + 1, alpha=lf("beta")),  # keyword override
    ],
)
def test_signature_injection_override(result):
    assert result == 21

# Mix implicit and explicit params
@pytest.mark.parametrize(
    "pair",
    [
        lfc(lambda alpha, beta: (alpha, beta), beta=lf("gamma")),  # first implicit, second explicit
        lfc(lambda alpha, beta: (alpha, beta), lf("gamma")),       # first explicit, second implicit
    ],
)
def test_signature_injection_mixed(pair):
    assert pair in [(10, 30), (30, 20)]

# 4) Defaults prevent implicit injection for that parameter
@pytest.mark.parametrize("val", [lfc(lambda alpha=999: alpha)])
def test_signature_injection_defaults(val):
    assert val == 999
```

Notes:
- Implicit injection only happens for parameters without an explicit value and without a default.
- Explicit arguments passed to `lfc` (positional or keyword) always take precedence over implicit injection.
- The mapping of positional explicit arguments follows the callable's parameter order.

## Contributing

Contributions are very welcome. Tests can be run with `pytest`.

## License

Distributed under the terms of the `MIT` license, `pytest-lazy-fixtures` is free and open source software

## Issues

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