Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pytest: parametrizing tests that require a slow initialization

Tags:

python

pytest

I want to do tests with randomized parameters of a class with a very slow init method. The tests themself are very quick, but require a time consuming initialization step. Of course. I do something like this:

@pytest.mark.parametrize("params", LIST_OF_RANDOMIZED_PARAMS)
def test_one(params):
    state = very_slow_initialization(params)
    assert state.fast_test()

@pytest.mark.parametrize("params", LIST_OF_RANDOMIZED_PARAMS)
def test_two(params):
    state = very_slow_initialization(params)
    assert state.another_fast_test()

From my unsuccessful tries so far I've learnt:

  1. initializing a Testclass with a parametrized set_class(params) method is not supported
  2. Using a fixture that initialized the class still calls the slow initialization every time
  3. I could create a list with all initialized states in advance, however they demand a lot of memory. Furthermore sometimes I like to rune a lot of randomized tests overnight and just stop them the next morning. This this I would need to know precisely how many tests I should to so that all initializations are finished before that.
  4. If possible I would prefer a solution that runs both tests for the first parameter, then runs both with the second parameter and so on.

There is probably a really simple solution for this.

like image 467
Smudoxo Avatar asked Nov 18 '25 05:11

Smudoxo


1 Answers

pytest fixtures is a solution for you. Lifetime of fixture might be a single test, class, module or whole test session.

fixture management scales from simple unit to complex functional testing, allowing to parametrize fixtures and tests according to configuration and component options, or to re-use fixtures across function, class, module or whole test session scopes.

Per Fixture availability paragraph, you need to define feature in class, or on module level.

Consider using module-scoped ones (pay attention, that initialization launched only once):

import pytest


@pytest.fixture(scope="module")
def heavy_context():
    # Use your LIST_OF_RANDOMIZED_PARAMS randomized parameters here
    # to initialize whatever you want.
    print("Slow fixture initialized")
    return ["I'm heavy"]


def test_1(heavy_context):
    print(f"\nUse of heavy context: {heavy_context[0]}")


def test_2(heavy_context):
    print(f"\nUse of heavy context: {heavy_context[0]}")

Tests output:

...
collecting ... collected 2 items

test_basic.py::test_1 Slow fixture initialized
PASSED                                             [ 50%]
Use of heavy context: I'm heavy

test_basic.py::test_2 PASSED                                             [100%]
Use of heavy context: I'm heavy

Now, if you need it to be assertion safe (release resources even when test fails), consider creating heavy_context in a context-manager manner (much more details here: Fixture, Running multiple assert statements safely):

import pytest


@pytest.fixture(scope="module")
def heavy_context():

    print("Slow context initialized")
    obj = ["I'm heavy"]

    # It is mandatory to put deinitialiation into "finally" scope
    # otherwise in case of exception it won't be executed
    try:
        yield obj[0]
    finally:
        print("Slow context released")


def test_1(heavy_context):
    # Pay attention, that in fact heavy_context now
    # is what we initialized as 'obj' in heavy_context
    # function.
    print(f"\nUse of heavy context: {heavy_context}")


def test_2(heavy_context):
    print(f"\nUse of heavy context: {heavy_context}")

Output:

collecting ... collected 2 items

test_basic.py::test_1 Slow context initialized
PASSED                                             [ 50%]
Use of heavy context: I'm heavy

test_basic.py::test_2 PASSED                                             [100%]
Use of heavy context: I'm heavy
Slow context released


============================== 2 passed in 0.01s ===============================

Process finished with exit code 0
like image 69
Stepan Dyatkovskiy Avatar answered Nov 19 '25 20:11

Stepan Dyatkovskiy



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!