how do you mock a global variable in pytest? Here is a pair of example files:
File being tested, call it main.py:
MY_GLOBAL = 1
def foo():
return MY_GLOBAL*2
def main()
# some relevant invokation of foo somewhere here
if __name__=='__main__':
main()
File that is testing, call it test_main.py:
from main import foo
class TestFoo(object):
def test_that_it_multiplies_by_global(self):
# expected=2, we could write, but anyway ...
actual = foo()
assert actual == expected
This is just a dummy example of course, but how would you go about mocking MY_GLOBAL and giving it another value? Thanks in advance i've been kind of breaking my head over this and i bet it's really obvious.
CodePudding user response:
The global variable is an attribute of the module, which you can patch using patch.object
:
import main
from unittest.mock import patch
class TestFoo(object):
def test_that_it_multiplies_by_global(self):
with patch.object(main, 'MY_GLOBAL', 3):
assert main.foo(4) == 12 # not 4
However, you want to make sure you are testing the right thing. Is foo
supposed to multiply its argument by 1 (and the fact that it uses a global variable with a value of 1
an implementation detail), or is it supposed to multiply its argument by whatever value MY_GLOBAL
has at the time of the call? The answer to that question will affect how you write your test.
CodePudding user response:
It's useful to distinguish between module-level constants and global variables. The former are pretty common, but the latter are an anti-pattern in Python. You seem to have a module-level constant (read-only access to the var in normal production code). Global variables (R/W access in production code) should generally be refactored if possible.
For module constants: If you can do so, it's generally more maintainable to refactor the functions that depend on module constants. This allows direct testing with alternate values as well as a single source of truth for the "constants" and backward compatibility. A minimal refactor is as simple as adding an optional parameter in each function that depends on the "constant" and doing a simple search-and-replace in that function, e.g.:
def foo(value=MY_GLOBAL):
return value*2
All other code can continue to call foo()
as normal, but if you want to write tests with alternate values of MY_GLOBAL
, you can simply call foo(value=7484)
.
If what you want is an actual global, (with the global
keyword and read/write access during production code, try these alternatives.