0

I'm writing a set of tests for methods of the object whose initialization may be altered by environment variables. I started by putting object creation into a fixture and making a bunch of tests

@pytest.fixture()
def my_object():
  # Initialise and return my object
  return new_object

def test_method_1(my_object): ...

def test_method_2(my_object): ...  # etc.

One of the tests needs to check the object's behaviour with an environment variable set, so I naively wrote:

def test_method_with_env(my_object):
  os.environ['VARIABLE'] = 'SPECIAL_VALUE'
  assert my_object.method_under_test()

which obviously fails because the variable must be set during object initialization, not after it.

I also tried

@pytest.fixture
def patch_env(request):
  marker = request.node.get_closest_marker('env')
  if marker and marker.kwargs:
    os.environ.update(marker.kwargs)

@pytest.mark.env(VARIABLE='SPECIAL_VALUE')
def test_method_with_env(patch_env, my_object):
  assert my_object.method_under_test()

but that also fails, because there is no guarantee that patch_env is applied before my_object fixture.

In the end, I could make my_object depend on patch_env but I only need to modify the env variable for this one test, not all of them. Is there any pytest-recommended way to go about it or am I misusing fixtures here?

jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
warownia1
  • 2,771
  • 1
  • 22
  • 30

1 Answers1

1

In pytest, fixtures are generally meant to provide a consistent setup for your tests and to encapsulate that setup, but it can be a bit tricky when you need to modify the environment specifically for one test.

You can define a custom fixture that is responsible for setting up and restoring the environment variable for your specific test case. This way, the environment variable will be set up only for the test that requires it, and the other tests won't be affected.

import os
import pytest

@pytest.fixture
def set_env_variable():
    original_value = os.environ.get('VARIABLE')
    os.environ['VARIABLE'] = 'SPECIAL_VALUE'
    yield
    if original_value is None:
        del os.environ['VARIABLE']
    else:
        os.environ['VARIABLE'] = original_value

def test_method_with_env(set_env_variable, my_object):
    assert my_object.method_under_test()

You can see that 'set_env_variable' fixture temporarily changes the environment variable for the duration of the test, ensuring that the global state is not affected. This approach doesn't rely on the order of fixture execution which allow you to isolate the changes to the environment variable for specific tests, minimizing potential side effects on other tests.

Remember that when working with global state changes in tests, it's important to ensure proper isolation and cleanup to maintain the reliability of your test suite.

You could also try to parametrize fixtures: https://docs.pytest.org/en/latest/how-to/parametrize.html

f05135
  • 56
  • 7
  • Your solution is the same as the one I presented and suffers from the same problem: there is no guarantee that `set_env_variable` is run before `my_object` fixture making it unreliable. – warownia1 Aug 28 '23 at 13:32