简体   繁体   中英

Testing constants declarations using pytest

We have a Python 3.7 application that has a declared constants.py file that has this form:

APP_CONSTANT_1 = os.environ.get('app-constant-1-value')

In a test.py we were hoping to test the setting of these constants using something like this (this is highly simplified but represents the core issue):

class TestConfig:
    """General config tests"""

    @pytest.fixture
    def mock_os_environ(self, monkeypatch):
        """  """

        def mock_get(*args, **kwargs):
            return 'test_config_value'

        monkeypatch.setattr(os.environ, "get", mock_get)

    def test_mock_env_vars(self, mock_os_environ):
        import constants
        assert os.environ.get('app-constant-1-value') == 'test_config_value' #passes
        assert constants.APP_CONSTANT_1 == 'test_config_value' #fails

The second assertion fails as constants.constants.APP_CONSTANT_1 is None. Turns out that the constants.py seems to be loaded during pytest's 'collecting' phase and thus is already set by the time the test is run.

What are we missing here? I feel like there is a simple way to resolve this in pytest but haven't yet discovered the secret. Is there some way to avoid loading the constants file prior to the tests being run? Any ideas are appreciated.

The problem is most likely that constants has been loaded before. To make sure it gets the patched value, you have to reload it:

import os
from importlib import reload

import pytest
import constants

class TestConfig:
    """General config tests"""

    @pytest.fixture
    def mock_os_environ(self, monkeypatch):
        """  """
        monkeypatch.setenv('app-constant-1-value', 'test_config_value')
        reload(constants)

    def test_mock_env_vars(self, mock_os_environ):
        assert os.environ.get('app-constant-1-value') == 'test_config_value'
        assert app.APP_CONSTANT_1 == 'test_config_value'

Note that I used monkeypatch.setenv to specifically set the variable you need. If you don't need to change all environment variables, this is easier to use.

Erm, I would avoid using constants. You can subclass os.environment for a start, and then use a mocked subclass for your unit tests, so you can have my_env.unique_env as a member variable. You can then use eg. import json to use a json configuration file without getting involved with hard coded python.

The subclass can then hold the relevant variables (or methods if you prefer)

Being able to add a facade to os.environment provides you with the abstraction you are looking for, without any of the problems.

Even is one is using a legacy/larger project, the advantage of using an adapter for access to the environment must be apparent.

Since you are writing unit tests, there is an opportunity to use an adapter class in both the tests and the functions being tested.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM