This question already has answers here:
Mock patching from/import statement in Python
(3 answers)
Closed 1 year ago.
I'm tring to use mock to test a specific function, for some reasons it seems that I can't mock twice the same module and I don't understand why
a.py
def a():
return "ok"
b.py
from sandbox.tests.a import a
def b(la):
for i in la:
a()
return "b"
def c():
a()
return "c"
What I want to test is the amount of calls of a are made, so in my test I do :
from mock import patch
#patch("sandbox.tests.a.a", return_value="mocked")
def test_b(mock):
from sandbox.tests.b import b
b(["1", "2"])
assert mock.call_count == 2
#patch("sandbox.tests.a.a", return_value="mocked2")
def test_b_again(mock):
from sandbox.tests.b import c
c()
assert mock.call_count == 1
If I run 1 test it works but when I run the 2 tests together I have this error :
E AssertionError: assert 0 == 1
E + where 0 = <Mock name='test_2' id='4360227664'>.call_count
Surprisingly if I print the output of the mock in each function b and c, the mock has the same ID :
a <MagicMock name='a' id='4358021024'> # function b
a <MagicMock name='a' id='4358021024'> # function c
I also tried to put a name on the mock but it stills gives the same id
#patch("sandbox.tests.a.a")
def test_b(mock):
mock = Mock(name="test_1", return_value="test_1")
from sandbox.tests.b import b
b(["1", "2"])
assert mock.call_count == 2
#patch("sandbox.tests.a.a")
def test_b_again(mock):
mock = Mock(name="test_2", return_value="test_2")
from sandbox.tests.b import c
c()
assert mock.call_count == 1
Is there an equivalent of jest.resetModules() or something so I can mock twice the same module ?
Thanks
The solution is to patch a within the context of module b, rather than trying to patch sandbox.tests.a 'directly'. This is necessary because module b imports a only once, rather than in each of functions b and c.
Instead, patch sandbox.tests.b.a - i.e the 'instance' of a which is being used by module b:
#patch("sandbox.tests.b.a", return_value="mocked")
def test_b(self, mock):
from sandbox.tests.b import b
b(["1", "2"])
assert mock.call_count == 2
#patch("sandbox.tests.b.a", return_value="mocked2")
def test_c(self, mock):
from sandbox.tests.b import c
c()
assert mock.call_count == 1
Related
While I was testing some code with pytest, I needed to monkeypatch a method that was also decorated. I patched the method and while I expected the decorators being executed in the patched method, they weren't, so it looks like the patched method replaces everything.
Is this the normal behaviour?
I wrote a simplified example of the problem:
functions.py
def mul_by(func):
def wrapper(num):
return func(num * 4)
return wrapper
#mul_by
def show_number(n: int):
print(n)
return n
test_functions.py
import sys
from src.functions import show_number
def test_show_number(monkeypatch):
def mocked_show_number(num: int):
print('Mocked func')
return 5
monkeypatch.setattr(sys.modules[__name__], 'show_number', mocked_show_number)
assert show_number(2) == 20
Output
collected 1 item
test/test_functions.py F [100%]
================================================================================================= FAILURES =================================================================================================
_____________________________________________________________________________________________ test_show_number _____________________________________________________________________________________________
monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x10b7e5d00>
def test_show_number(monkeypatch):
def mocked_show_number(num: int):
print('Mocked func')
return 5
monkeypatch.setattr(sys.modules[__name__], 'show_number', mocked_show_number)
> assert show_number(2) == 20
E assert 5 == 20
E + where 5 = show_number(2)
test/test_functions.py:11: AssertionError
------------------------------------------------------------------------------------------- Captured stdout call -------------------------------------------------------------------------------------------
Mocked func
========================================================================================= short test summary info ==========================================================================================
FAILED test/test_functions.py::test_show_number - assert 5 == 20
The decorator simply multiplies the number by 4, and in the monkeypatched method I hardcode the value just to return 5, so I'd expect 20 in the result. But, 5 is returned, the monkeypatched method's value.
Can someone point if there is any error in this way to monkeypatch or if it is the desired behaviour?
If the second parameter of assert is used normally it prints it out debug information.
E.g. with the following code:
class TestTest:
def test_test(self):
assert 1 == 2, "TEST"
The following debug information is printed:
tests\test_test.py:1 (TestTest.test_test)
1 != 2
Expected :2
Actual :1
<Click to see difference>
self = <tests.test_test.TestTest object at 0x000002147E5B1B08>
def test_test(self):
> assert 1 == 2, "TEST"
E AssertionError: TEST
E assert 1 == 2
E +1
E -2
test_test.py:3: AssertionError
However, if the assert happens in a helper function, this is not printed:
class TestTest:
def test_test(self):
self.verify(1)
def verify(self, parameter):
assert 1 == 2, "TEST"
results in:
tests\test_test.py:1 (TestTest.test_test)
1 != 2
Expected :2
Actual :1
<Click to see difference>
self = <tests.test_test.TestTest object at 0x000002AFDF1D5AC8>
def test_test(self):
> self.verify(1)
test_test.py:3:
So the stack trace is incomplete and the debug information not shown.
UPDATE:
This only happens when starting the test through PyCharm (2021.3).
Is there any way to fix this?
I'm having some issue while creating unittest for internal parameter.
My structure is:
[1] my_animal.py contains Myclass and method: do_bite()
my_animal.py
class Myclass():
def do_bite(self):
return 1
[2] my_module.py contains jobMain("") which is using the method from my_animal.py
my_module.py
import sys
from someclass import Myclass
def jobMain(directoryPath):
flag = -1
result = Myclass()
if result.do_bite() is None:
flag = 0
if result.do_bite() is 1:
flag = 1
if result.do_bite() is 2:
flag = 2
[3] my_test.py contains the unittest to test jobMain in my_module.py
my_test.py
# Mock Myclass.dobite to None
#pytest.fixture
def mock_dobite0():
with mock.patch('my_module.Myclass') as mocked_animal:
mocked_animal.return_value.do_bite.return_value = None
yield
# Mock Myclass.dobite to 1
#pytest.fixture
def mock_dobite1():
with mock.patch('my_module.Myclass') as mocked_animal:
mocked_animal.return_value.do_bite.return_value = 1
yield
# Mock Myclass.dobite to 2
#pytest.fixture
def mock_dobite2():
with mock.patch('my_module.Myclass') as mocked_animal:
mocked_animal.return_value.do_bite.return_value = 2
yield
# My unittest to test dobite() method
def test_dobite0(mock_Myclass, mock_dobite0):
jobMain("")
def test_dobite1(mock_Myclass, mock_dobite1):
jobMain("")
def test_dobite2(mock_Myclass, mock_dobite2):
jobMain("")
My question is: How to test 'flag' parameter inside JobMain?
'flag' para must be assigned the correct value.( eg: dobite = 1 => flag = 1)
The variable para only exists in the scope of jobMain. If you want to use the variable outside jobMain the most common ways are
1) return the value
This is quite obvious. Since jobMain is a function, it returns a value. Without an explicit return statement you return None. You could just
def jobmain(pth):
# do stuff and assign flag
return flag
# and inside tests
assert jobmain("") == 1
2) Use a class instead
If you want the jobMain to remember some state, then it is common practice to use objects. Then flag would be attribute of the object and could be accessed from outside, after you call any method (function) of JobMain. For example
class JobMain:
def __init__(self):
self.flag = -1
def run(self, pth):
result = Myclass()
if result.do_bite() is None:
self.flag = 0
if result.do_bite() is 1:
self.flag = 1
if result.do_bite() is 2:
self.flag = 2
# and inside test
job = JobMain()
job.run()
assert job.flag == 1
Note
I just copy-pasted your code for setting the flag. Note that you call do_bite() many times, if the resulting value is None or 1. Also, when testing against a number, one should use == instead of is.
How to test 'flag' parameter inside JobMain?
You don't. It's an internal variable. Testing it would be glass-box testing; the test will break if the implementation changes.
Instead, test the effect of flag. This is black-box testing. Only the interface is tested. If the implementation changes the test still works allowing the code to be aggressively refactored.
Note: If you don't hard code result = Myclass() you don't need to mock. Pass it in as an argument with the default being Myclass().
def jobMain(directoryPath, result=Myclass()):
Then you don't need to patch Myclass(). Instead, pass in a mock object.
# I don't know unittest.mock very well, but something like this.
mock = Mock(Myclass)
mock.do_bite.return_value = 2
jobMain('', result=mock)
This also makes the code more flexible outside of testing.
I am planning to use pytest and pytest-mock for validating the Python code. Being a newbie, wrote a sample code to validate the mock on class and seeing failure. I am wondering what went wrong.
src/main.py
class Main(object):
def __init__(self, my_var=None):
self.var = my_var
def internal_func(self, var=10):
my_var = var + 20
return my_var
def test_func(self):
val = self.internal_func(20)
return val + 40
tests/test_main.py
import pytest
from pytest_mock import mocker
from src.main import Main
def new_func(cls, *args, **kwargs):
return 2
def test_main_mock(mocker):
mocker.patch.object(Main, 'internal_func')
val = Main().test_func()
assert Main.internal_func.assert_called_with(20)
It fails with the following error
======================================================================================== FAILURES ========================================================================================
_____________________________________________________________________________________ test_main_mock _____________________________________________________________________________________
mocker = <pytest_mock.MockFixture object at 0x7f34f490d8d0>
def test_main_mock(mocker):
mocker.patch.object(Main, 'internal_func')
main = Main()
val = main.test_func()
# assert val == 80
> assert Main.internal_func.assert_called_with(20)
E AssertionError: assert None
E + where None = <bound method MagicMock.wrap_assert_called_with of <MagicMock name='internal_func' id='139865418160784'>>(20)
E + where <bound method MagicMock.wrap_assert_called_with of <MagicMock name='internal_func' id='139865418160784'>> = <MagicMock name='internal_func' id='139865418160784'>.assert_called_with
E + where <MagicMock name='internal_func' id='139865418160784'> = Main.internal_func
tests/test_main.py:13: AssertionError
The return_value or side_effect must be set before the patched func take effect
def test_main_mock(mocker):
# mock internal_func of class Main
mocked_internal_func = mocker.patch.object(Main, 'internal_func')
# assign return_value or side_effect
mocked_internal_func.return_value = -10
# class instance
ma = Main()
val = ma.test_func()
assert ma.internal_func.assert_called_with(20)
Correction of mistake, the assert should not be used together with assert_called_with, they are independent assert.
assert val == 30
mocked_internal_func.assert_called
ma.internal_func.assert_called_with(20)
mocked_internal_func.assert_called_with(20)
I have a test suite (using nose, not unittest), and I want to patch a function to return a specific sequence of values for every test in the test class. My first attempt, using a simplified example, was:
#patch('time.clock', MagicMock(side_effects=[1, 2]))
class Tests:
def test_1(self):
assert time.clock() == 1
assert time.clock() == 2
def test_2(self):
assert time.clock() == 1
assert time.clock() == 2
However, the MagicMock instance is only created once, so the second test fails when the side effects run out. I can patch each test method separately, but I don't really want to duplicate the patch decorator over all of them (there are a lot more tests than in this example!) The other way I could do it is to create the patch in the setup code like this:
class Tests:
def setup(self):
self.old_clock = time.clock
time.clock = MagicMock(side_effects=[1, 2])
def teardown(self):
time.clock = self.old_clock
def test_1(self):
assert time.clock() == 1
assert time.clock() == 2
def test_2(self):
assert time.clock() == 1
assert time.clock() == 2
But saving and restoring the original function definition seems like something that Mock should be able to do automatically. Is there another method of doing this that I'm missing? Or is my last example the best way of doing this?
a = (x for x in [1,2])
x = lambda : next(a)
x()
Out: 1
x()
Out: 2
Put your answers into a's list.
Change X for your desired name.
You should just apply the patch to every test, instead of applying it to the class:
class Tests:
#patch('time.clock', MagicMock(side_effects=[1, 2]))
def test_1(self):
assert time.clock() == 1
assert time.clock() == 2
#patch('time.clock', MagicMock(side_effects=[1, 2]))
def test_2(self):
assert time.clock() == 1
assert time.clock() == 2