Python 3 unittest cannot mock method used inside functions - python

I am trying unit test in Python.
test.py
from unittest import TestCase,main
from unittest.mock import patch
import file
def mock_return(*args):
return -1
class Tests(TestCase):
#patch("file.Foo.a", side_effect=mock_return)
def test_1(self, mock_fun):
self.assertEqual(file.Foo().a(), -1)
#patch("os.urandom", side_effect=mock_return)
def test_2(self, mock_fun):
self.assertEqual(file.Foo().b(), -1)
if __name__ == "__main__":
main()
file.py
from os import urandom
class Foo:
def a(self):
return 1
def b(self):
return urandom(1)
Why test_1 passed but test_2 failed? Is there a way to mock a method used by other class?

You must patch the binding used by the function being tested.
from os import urandom # in file.py
binds name urandom to the function os.urandom in the file module. Foo.b accesses the function via the file.urandom binding. So the test for Foo.b must patch file.urandom, not os.urandom.

Related

How to mock a function which gets executed during the import time?

Here the ABC() and obj.print_1() get called during the import time and it prints "making object" and "printed 1" respectively. How can we mock all the three functions, __init__(), print_1(), and print_2()?
xyz.py
from abc import ABC
obj = ABC()
obj.print_1()
def func():
return obj.print_2(2)
abc.py
class ABC():
def __init__(self):
print("making object")
def print_1(self):
print("printed 1")
return None
def print_2(self, val):
print("printed ", val)
return None
Indeed, as soon as you import xyz, it will import abc and create an instance then call a method on it.
Solution : import abc yourself BEFORE xyz EVER GETS IMPORTED, and mock the methods defined in the class. And because we can't import a method, patch.object is required.
Note : I added a self as parameter in your ABC.print_1 method, otherwise it would be incorrect. Otherwise make it #staticmethod
Here is the test file I used :
import unittest
import unittest.mock as mock
from so74709409_abc import ABC
# no import of `xyz` here !
class Tests(unittest.TestCase):
def test__xyz_obj_calls_print1(self):
# __init__ must return None
with mock.patch.object(ABC, "__init__", **{"return_value": None}) as mock_init, \
mock.patch.object(ABC, "print_1") as mock_print1, \
mock.patch.object(ABC, "print_2") as mock_print2:
from so74709409_xyz import func # import now !
func()
mock_init.assert_called_once()
mock_print1.assert_called_once_with()
mock_print2.assert_called_once_with(2)
if __name__ == "__main__":
unittest.main()
But this is not very robust, if the module was already imported (maybe indirectly) before the test run, the import inside the test won't have any effect, and so it will fail (mocks not getting called). It can be a pain in a real test suite (with many tests running in sequence) because the previous test will already have imported xyz.
That's why it's better to do these kind of things in a if __name__=="__main__", or in a function called deliberately.
(beware : I assume you choose abc as a dummy name, but it is actually a standard library module for Abstract Base Classes)

Mocking a method inside class in python test

I am new to Python and writing a unit test for the already existing code.
I have the following Python code structure:
root_project:
-- src
-- signUp
-- __init__.py
-- abc.py
-- xyz.py
abc.py contains the following code:
from . import xyz
class Abc:
def __init__(self):
pass
def start_process(self):
xyz.start_timer()
I want to mock xyz.start_timer() inside method start_process of class Abc so that start_timer() mock version can be used.
How can I achieve this?
You can simply patch the start_timer function using mock.patch().
abc.py
import xyz
class Abc:
def __init__(self):
pass
def start_process(self):
xyz.start_timer()
test_abc.py
from unittest import mock, TestCase
from abc import Abc
class Test_Abc(TestCase):
#mock.patch("abc.start_timer")
def test_start_process(self, mock_start_timer):
# your test code
Notice that I have patched abc.start_timer and not xyz.start_timer.
You just need to patchxyz.start_timer(), I just added return to start_process to show that the patching really does what it should:
import xyz
import unittest
from unittest import mock
class Abc:
def __init__(self):
pass
def start_process(self):
return xyz.start_timer()
class Test_Abc(unittest.TestCase):
#mock.patch('xyz.start_timer', return_value=True)
def test_start_process(self, start_process):
a = Abc()
self.assertTrue(a.start_process())
if __name__ == '__main__':
unittest.main()
Out:
.
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK

Check if Path.exists() was called once with mock.path in a unittest

I need to know if Path.exists() was called. I want to do that in a unittest but allways got the answer that it was not called.
I assume something is wrong with the import path. I know from the docs and some blog-posts that this can be tricky.
This is the "productive" code:
import pathlib
class Foo():
def __init__(self, file_path: pathlib.Path):
if not file_path.exists():
print('Not good.')
else:
print('Fine.')
And this is the unittest for it
import unittest
import unittest.mock
import pathlib
import mycode
class MyTest(unittest.TestCase):
#unittest.mock.patch('pathlib.Path')
def test_mycode(self, mock_path):
mypath = pathlib.Path('bar')
foo = mycode.Foo(mypath)
mock_path.exists.assert_called_once()
But the error is still
AssertionError: Expected 'exists' to have been called once. Called 0
times.
You can create mock for pathlib.Path using create_autospec helper function. And pass this mock object to the constructor of the Foo class.
Functions or methods being mocked will have their arguments checked to ensure that they are called with the correct signature.
E.g. (Python 3.9.6)
foo.py:
import pathlib
class Foo():
def __init__(self, file_path: pathlib.Path):
if not file_path.exists():
print('Not good.')
else:
print('Fine.')
foo_test.py:
import unittest
import pathlib
from unittest.mock import create_autospec
from foo import Foo
class MyTest(unittest.TestCase):
def test_Foo_file_path_exists(self):
mock_path = create_autospec(pathlib.Path)
mock_path.exists.return_value = True
Foo(mock_path)
mock_path.exists.assert_called_once()
def test_Foo_file_path_not_exists(self):
mock_path = create_autospec(pathlib.Path)
mock_path.exists.return_value = False
Foo(mock_path)
mock_path.exists.assert_called_once()
if __name__ == '__main__':
unittest.main()
Test result:
Fine.
.Not good.
.
----------------------------------------------------------------------
Ran 2 tests in 0.184s
OK
Name Stmts Miss Cover Missing
----------------------------------------------------------------------
src/stackoverflow/71945781/foo.py 6 0 100%
src/stackoverflow/71945781/foo_test.py 17 0 100%
----------------------------------------------------------------------
TOTAL 23 0 100%
The answer of #slideshowp2 is fine and working. And it pointed me to the right solution better fitting to my own MWE.
class MyTest(unittest.TestCase):
#unittest.mock.patch('pathlib.Path')
def test_mycode(self, mocked_path):
foo = mycode.Foo(mocked_path)
mocked_path.exists.assert_called_once()
The problem with my code was that I instantiated a real object of pathlib.Path. But the key of the solution is to use the mock object mocked_path because this is a surrogate for a real Path object.
In my questions intial code the Foo.__init__() never used the mocked object but the real pathlib.Path.

unittest mock how to mock a method being called

I want to test that method a calls method b. These methods are in separate files, and are not a part of class object.
# file_a.py
from file_b import b
def a():
b()
# file_b.py
def b():
test
import unittest
from unittest import mock
from file_a import a
class MyTestCase(unittest.TestCase):
#mock.patch('file_b.b')
def test_b_called(self, mock):
a()
mock.assert_called()
if __name__ == "__main__":
unittest.main()
This fails with AssertionError: Expected 'b' to have been called.
Is there a right way to do this?
When you import a function into the current namespace, like in your example, the function will need to be patched in that namespace. In your case, you need:
#mock.patch('file_a.b')
You would patch file_b.b if you had done the import and use like this:
import file_b
def a():
file_b.b()

Mocking Functions Using Python Mock

I am trying to Mock a function (that returns some external content) using the python mock module.
I'm having some trouble mocking functions that are imported into a module.
For example, in util.py I have
def get_content():
return "stuff"
I want to mock util.get_content so that it returns something else.
I am trying this:
util.get_content=Mock(return_value="mocked stuff")
If get_content gets invoked inside another module, it never actually seems to return the mocked object. Am I missing something in terms of how to use Mock?
Note that if I invoke the following, things work correctly:
>>> util.get_content=Mock(return_value="mocked stuff")
>>> util.get_content()
"mocked stuff"
However, if get_content is called from inside another module, it invokes the original function instead of the mocked version:
>>> from mymodule import MyObj
>>> util.get_content=Mock(return_value="mocked stuff")
>>> m=MyObj()
>>> m.func()
"stuff"
Contents of mymodule.py
from util import get_content
class MyObj:
def func():
get_content()
So I guess my question is - how do I get invoke the Mocked version of a function from inside a module that I call?
It appears that the from module import function may be to blame here, in that it doesn't point to the Mocked function.
The general case would be to use patch from mock. Consider the following:
utils.py
def get_content():
return 'stuff'
mymodule.py
from util import get_content
class MyClass(object):
def func(self):
return get_content()
test.py
import unittest
from mock import patch
from mymodule import MyClass
class Test(unittest.TestCase):
#patch('mymodule.get_content')
def test_func(self, get_content_mock):
get_content_mock.return_value = 'mocked stuff'
my_class = MyClass()
self.assertEqual(my_class.func(), 'mocked stuff')
self.assertEqual(get_content_mock.call_count, 1)
get_content_mock.assert_called_once()
Note how get_content is mocked, it is not util.get_content, rather mymodule.get_content since we are using it in mymodule.
Above has been tested with mock v2.0.0, nosetests v1.3.7 and python v2.7.9.
I think I have a workaround, though it's still not quite clear on how to solve the general case
In mymodule, if I replace
from util import get_content
class MyObj:
def func():
get_content()
with
import util
class MyObj:
def func():
util.get_content()
The Mock seems to get invoked. It looks like the namespaces need to match (which makes sense). However, the weird thing is that I would expect
import mymodule
mymodule.get_content = mock.Mock(return_value="mocked stuff")
to do the trick in the original case where I am using the from/import syntax (which now pulls in get_content into mymodule). But this still refers to the unmocked get_content.
Turns out the namespace matters - just need to keep that in mind when writing your code.
You have to patch the function where it is being used. In your case that would be in the mymodule module.
import mymodule
>>> mymodule.get_content = Mock(return_value="mocked stuff")
>>> m = mymodule.MyObj()
>>> m.func()
"mocked stuff"
There is a reference in the docs here: http://docs.python.org/dev/library/unittest.mock.html#where-to-patch
Let's assume you're creating your mock inside module foobar:
import util, mock
util.get_content = mock.Mock(return_value="mocked stuff")
If you import mymodule and call util.get_content without first importing foobar, your mock will not be installed:
import util
def func()
print util.get_content()
func()
"stuff"
Instead:
import util
import foobar # substitutes the mock
def func():
print util.get_content()
func()
"mocked stuff"
Note that foobar can be imported from anywhere (module A imports B which imports foobar) as long as foobar is evaluated before util.get_content is called.
While it doesn't provide an answer to your question directly, another possible alternative is to transform your function to a static method using the #staticmethod.
So you could transform your module utils into a class using something like:
class util(object):
#staticmethod
def get_content():
return "stuff"
Then mock patches it correctly.

Categories