How to use fixture value inside pytest parametrize params

52 Views Asked by At

I want to use a fixture called "time_period" inside of my parametrized params. The fixture returns an object TimePeriod that stores 2 dates "start" and "end". I need to write a test that checks if my function "filter_data_by_time_horizon" removes all the rows in a pyspark dataframe outside of the given time period. For that I want to parametrize my test to cover all the edge cases:

@pytest.mark.parametrize("date_from, date_to", [
             (time_period.start - relativedelta(days=4), time_period.start - relativedelta(days=2)),
             (time_period.end + relativedelta(days=4), time_period.end + relativedelta(days=2)),
    ])
def test_filter_data_by_time_horizon(factory, date_from, date_to):
    test_data = filter_data_by_time_horizon(factory_batch_to_dataframe(factory.create_batch(
        size=2,
        date_from=date_from,
        date_to=date_to,
    )))
    assert test_data.count() == 0

However I can't use the time_period fixture the way I wrote it in the code. I could techincally declare it as a variable outside of the testcase, but that doesn't seem like a clean solution. Btw the time_period fixture is stored in a module scoped conftest.py. How do I cleanly approach it?

1

There are 1 best solutions below

0
tmt On

A clean approach would be to create another fixture that would build your parameters. Fixtures can use other fixtures and indirect=True argument in parametrization causes a call to your fixture including the defined parameters. To access those parameters you need to use the fixture request and access its param property.

Here is a rough untested draft that should give you the idea how to approach it:

from functools import reduce
from operator import add, sub

@pytest.fixture()
def time_horizon(time_period, request):
    tp = getattr(time_period, request.param[0])
    tp_from = reduce(request.param[1], (tp, relativedelta(**request.param[2])))
    tp_to = reduce(request.param[1], (tp, relativedelta(**request.param[3])))

    return (tp_from, tp_to)

# Each parametrization set consists of:
# 0 - property of `time_period` so that you can differentiate 
#     between `start` and `end`
# 1 - operation to perform on that property
# 2 - unit an value used to calculate `date_from`
# 3 - unit an value used to calculate `date_to`
@pytest.mark.parametrize(
    "time_horizon",
    [('start', sub, {days=4}, {days=2}), ('end', add, {days=4}, {days=2})],
    indirect=True
)
def test_filter_data_by_time_horizon(time_horizon, factory):
    test_data = filter_data_by_time_horizon(factory_batch_to_dataframe(factory.create_batch(
        size=2,
        date_from=time_horizon[0],
        date_to=time_horizon[1],
    )))
    assert test_data.count() == 0