Reputation: 265
I have been working with Python's unittest.mock
library quite a bit, but right now I'm struggling with a use case that may not be approached correctly.
Consider a file mymodule/code.py
containing the following snippet:
def sum():
pass
def mul():
pass
def div():
pass
def get_functions():
return [sum, mul, div]
def foo():
functions = get_functions()
for func in functions:
func()
I want to test the foo
function, patching the sum
function, and leaving mul
and div
as they are. This is what I tried initially:
class TestFoo(unittest.TestCase):
@mock.patch('mymodule.code.foo.sum')
def test_foo(foo_sum_mock):
foo()
foo_sum_mock.assert_called_once()
However, the patching approach illustrated above does not work. I believe that the sum
function is patched correctly when loading mymodule.code.py
, but redefined due to the def sum()
block.
By reading the official documentation, I also tried to use the start
and stop
functions of the unittest.mock
library as follows:
def test_foo():
patcher = mock.patch('module.code.sum')
mocked_sum_fun = patcher.start()
foo()
mocked_sum_fun.assert_called_once()
mock_sum_fun.stop()
This approach also did not work. I was hoping it would avoid the sum
function override after the modules/code.py
file gets loaded.
Is it possible to patch a local function such as sum
? Or is moving the sum
function to another file the only option for patching?
Many thanks in advance!
Upvotes: 17
Views: 5002
Reputation: 6575
You can mock a function of same module using mock.patch
and refering this module as __main__
code.py
from unittest.mock import patch
def sum():
print("called method sum")
pass
def call_sum():
sum()
def return_mock():
print("I'm a mocked method")
return True
with patch('__main__.sum', return_value=return_mock()) as mock_test:
call_sum()
mock_test.assert_called_once() # assure that mocked method was called, not original.
You could also use the path of lib (my_project.code.sum
) instead of __main__.sum
.
Upvotes: 6
Reputation: 55
Generally speaking, you'd want to separate your test code from your production code:
code.py
def sum():
pass
def mul():
pass
def div():
pass
def get_functions():
return [sum, mul, div]
def foo():
functions = get_functions()
for func in functions:
func()
code_test.py
import unittest
import mock_test as mock
import code
class TestFoo(unittest.TestCase):
@mock.patch('code.sum')
def test_foo(self, sum_mock):
def new_sum_mock(*args, **kwargs):
# mock code here
pass
sum_mock.side_effect = new_sum_mock
code.foo()
sum_mock.assert_called_once()
But yes, you could place it all into one file:
code_test.py:
import unittest
import mock_test as mock
import code
def sum():
pass
def mul():
pass
def div():
pass
def get_functions():
return [sum, mul, div]
def foo():
functions = get_functions()
for func in functions:
func()
class TestFoo(unittest.TestCase):
@mock.patch('code_test.sum')
def test_foo(self, sum_mock):
def new_sum_mock(*args, **kwargs):
# mock code here
pass
sum_mock.side_effect = new_sum_mock
code.foo()
sum_mock.assert_called_once()
Upvotes: -3