4

I have tests on a function that uses and modifies global variables. I would like to make sure that my global variable is reset between my tests. Any trick to do that?

main.py:

y = 0


def inc(x):
    # side effect
    global y
    y = y + 1
    return x + y + 1

test_main.py:

from main import inc


def test_answer():
    assert inc(3) == 5


def test_answer_again():
    assert inc(3) == 5
_________________________________________________________________________________________ test_answer_again __________________________________________________________________________________________

    def test_answer_again():
>       assert inc(3) == 5
E       assert 6 == 5
E        +  where 6 = inc(3)

test_main.py:8: AssertionError
====================================================================================== short test summary info =======================================================================================
FAILED test_main.py::test_answer_again - assert 6 == 5
==================================================================================== 1 failed, 1 passed in 0.01s =====================================================================================
poiuytrez
  • 21,330
  • 35
  • 113
  • 172
  • 3
    Global mutable variables are considered bad design, and this is one of the reasons. But your test can just say `import main` and then `main.y = 0` if you really must. Or create a dedicated function in `main` to do just that. – Thomas Sep 27 '22 at 09:44
  • Does it answer your question ? https://stackoverflow.com/questions/423379/using-global-variables-in-a-function – Bloodbee Sep 27 '22 at 10:10

2 Answers2

4

Here is how you can use a simple fixture to ensure that the value of y is unchanged after each test:

import pytest

import main

from main import inc


@pytest.fixture
def set_y(request):
    y_before = main.y
    y_value_to_set = getattr(request, 'param', y_before)  # optional parameter
    main.y = y_value_to_set
    yield  # allows us to have cleanup after the test
    main.y = y_before  # once test is done, revert value for next test


def test_answer(set_y):
    assert inc(3) == 5


def test_answer_again(set_y):
    assert inc(3) == 5


@pytest.mark.parametrize('set_y', [20], indirect=["set_y"])
def test_answer_with_specific_y(set_y):
    assert inc(3) == 25

You can also add the autouse=True to your fixture if you want to prevent the need to specifically mention the fixture in every test and prevent bugs due to missing to specify it:

import pytest

import main

from main import inc


@pytest.fixture(autouse=True)
def set_y(request):
    y_before = main.y
    y_value_to_set = getattr(request, 'param', y_before)  # optional parameter
    main.y = y_value_to_set
    yield  # allows us to have cleanup after the test
    main.y = y_before  # once test is done, revert value for next test


def test_answer():
    assert inc(3) == 5


def test_answer_again():
    assert inc(3) == 5


@pytest.mark.parametrize('set_y', [20], indirect=["set_y"])
def test_answer_with_specific_y():
    assert inc(3) == 25
Peter K
  • 1,959
  • 1
  • 16
  • 22
0

It's a while ago, but if you, like me, have a settings module that you want to overwrite for the purpose of a test, you can use the following paradigm, which reloads the settings module from disk after running each test.

my_package/src/settings.py

any_variable = "rukwinden"

my_package/tests/tests_thing.py

from importlib import reload
from my_package import settings

@pytest.fixture(autouse=True)  # autouse will insert it into every test in this scope. You may not want that
def overwrite_config():
    """Overwrites global config and resets it after tests"""
    global settings
    settings.any_variable = "balhoofd"
    yield
    settings = reload(__import__("my_package").settings)
bluppfisk
  • 2,538
  • 3
  • 27
  • 56