Search code examples
pythonpytestpython-mockpytest-mock

How to mock a constant value


I have a structure like so:

mod1
├── mod2
│   ├── __init__.py
│   └── utils.py
└── tests
    └── test_utils.py

where

  • __init__.py:
CONST = -1
  • utils.py:
from mod1.mod2 import CONST

def mod_function():
    print(CONST)
  • test_utils.py:
from mod1.mod2.utils import mod_function

def test_mod_function(mocker):
    mock = mocker.patch("mod1.mod2.CONST")
    mock.return_value = 1000
    mod_function()

I'm using pytest with mocker.

By running python -m pytest -s ./mod1/tests I expected to see 1000 as an output, but got -1. Why?

How to patch a constant from the __init__.py file?


Solution

  • What patch("mod1.mod2.CONST") does is really to set the CONST attribute of the module object mod1.mod2 to a different object. It does not affect any existing names referencing the original mod1.mod2.CONST object.

    When utils.py does:

    from mod1.mod2 import CONST
    

    it creates a name CONST in the namespace of the mod1.mod2.utils module, referencing the object -1 currently referenced by the mod1.mod2.CONST name.

    And when test_mod_function then does:

    mock = mocker.patch("mod1.mod2.CONST")
    

    it modifies mod1.mod2 such that its CONST attribute now references a Mock object, while the CONST name in mod1.mod2.utils continues to reference the object -1, which is why setting mock.return_value does not affect the outcome of mod_function.

    To properly test mod_function with a mock CONST you can either patch CONST in the namespace where mod_function is defined:

    mock = mocker.patch("mod1.mod2.utils.CONST")
    

    or you can defer the import of mod1.mod2.utils until mod1.mod2.CONST has been patched:

    def test_mod_function(mocker):
        mock = mocker.patch("mod1.mod2.CONST")
        mock.return_value = 1000
        from mod1.mod2.utils import mod_function
        mod_function()