'Custom mocking for testing with pytest
I have a code base (shown below), with a src directory containing the code, that I have developed, alongside with some supporting Third Party libs in tp directory.
.
├── src
│ ├── f.py
│ └── __init__.py
├── tp
│ ├── __init__.py
│ └── sim.py
│
└── tests
# contents of src/f.py
#!/usr/bin/python
from tp.sim import sim
def somefunc():
sim()
# contents of tp/sim.py
#!/usr/bin/python
def sim():
print("Hello, I'm in sim.sim()")
I intend to write tests for the code inside src directory. As its visible with the sample files, the code in src directory relies on the files in tp. For majority of functions in tp, I intend to monkeypatch them for testing. A example of the test file is shown below.
1 #!/usr/bin/python
2
3
4 import src
5 from src.f import somefunc
6 from tp.sim import sim
7
8
9 def deco(func):
10 def wrapper(*args, **kwargs):
11 print("I'm inside wrapper")
12 func(*args, **kwargs)
13
14 return wrapper
15
16
17 sim = deco(sim)
18
19 # setattr(src.f, 'sim', sim)
20 somefunc()
In the above code, when monkeypatching the tp.sim.sim, doesn't affect the behaviour for src.f.somefunc (line 20). And only when we explicity patch the function sim in the namespace src.f.sim (line 19), do we see the patched function.
I have multiple files inside src directory, which import files from within tp directory and from src which in turn import from tp files.
My question primarily is, How do I ensure that my monkeypatched functions is always the one used during my testing (i.e. if I patch tp.sim.sim() with mysim() in my test setup, then all the test in that scope always see mysim())
Solution 1:[1]
If your tested code imports:
from tp.sim import sim
The f module will have a global sim name pointing to the sim function. Changing sim in the globals of tp.sim won't magically update the global name sim in the module f, so f.sim will always point to the real function.
So if you want to monkeypatch the sim function anyway without changing your import you can try to patch the global sim name of the f module, meaning: src.f.sim.
It would be easier to understand by changing the import in f as:
from tp import sim
In which case src.f gets a sim global name pointing to the sim module, module in which function can get modified by your current patching method. No duplicated sim function name, no surprises.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Julien Palard |
