Python unit tests - mocking imported class methods - python

I would like to mock some imported class methods and module functions for my unit tests. I tried several ways to define the mocked values but I don't understand why they are not taken into account.
I wrote some tests following the advices in Python Mocking a function from an imported module.
Here is a piece of code representing the application to test:
from services import myModule1
from services.spec1 import importedClass
class myClass(object):
def __init__(self, param1, param2):
self.param1 = param1
self.param2 = param2
self.param3 = 0
self.param4 = 0
self.myMethod()
def myMethod(self):
newVar = importedClass()
self.param3 = newVar.meth1(self.param2)
calcParam = myModule1.methodMod1(self.param1)
self.param4 = calcParam["keyParam3"]
I would like to unit test myMethod and I need to mock importedClass and myModule1.methodMod1().
Here is a new piece of code that I tried for the tests (previous attempts below):
import unittest
from unittest.mock import patch
from my_module import myClass
class test_myClass(unittest.TestCase):
#patch('my_module.importedClass')
#patch('my_module.myModule1')
def test_myMethod(self, mock_mod1, mock_class):
mock_mod1.methodMod1.return_value = {"keyParam3": 5, "keyParam4": 7}
mock_class.meth1.return_value = 2
test_parameters = (0, 0)
test_res = myClass(*test_parameters)
self.assertEqual(test_res.param3, 2)
self.assertEqual(test_res.param4, 5)
if __name__ == '__main__':
unittest.main()
The mocking has no error but the mocked values are not taken into account.
Previous attempts
What I tried for each of them, using 2 different approaches:
import unittest
from unittest.mock import Mock, patch
from my_module import myClass
import services
class test_myClass(unittest.TestCase):
def setUp(self):
services.spec1 = Mock()
services.spec1.importedClass.meth1.return_value = 2
#patch('services.myModule1')
def test_myMethod(self, my_mock):
my_mock.methodMod1.return_value = {"keyParam3": 5, "keyParam4": 7}
test_parameters = (0, 0)
test_res = myClass(*test_parameters)
self.assertEqual(test_res.param3, 2)
self.assertEqual(test_res.param4, 5)
if __name__ == '__main__':
unittest.main()
The result is that the calculated attributes are not updated and still 0 - so test fails.
I also tried with services = Mock() and defined return values for each part, to regroup each mock in setUp method or in a #patch, but nothing worked.
I also tried with my_module.spec1 = Mock(), to make the function global, or even self.spec1 = Mock() to make it very local to the test's context (if I understood correctly the differences, this is something I'm not really sure neither) but nothing worked.

To mock just the method:
class test_myClass(unittest.TestCase):
# #patch('my_module.importedClass') # Change this
#patch('my_module.importedClass.meth1') # to this
#patch('my_module.myModule1')
# def test_myMethod(self, mock_mod1, mock_class): # Change this
def test_myMethod(self, mock_mod1, mock_class_meth1): # to this
mock_mod1.methodMod1.return_value = {"keyParam3": 5, "keyParam4": 7}
# mock_class.meth1.return_value = 2 # Change this
mock_class_meth1.return_value = 2 # to this
test_parameters = (0, 0)
test_res = myClass(*test_parameters)
self.assertEqual(test_res.param3, 2)
self.assertEqual(test_res.param4, 5)
To mock the class and its method:
https://docs.python.org/3/library/unittest.mock-examples.html#mocking-classes
class test_myClass(unittest.TestCase):
#patch('my_module.importedClass')
#patch('my_module.myModule1')
def test_myMethod2(self, mock_mod1, mock_class):
mock_mod1.methodMod1.return_value = {"keyParam3": 5, "keyParam4": 7}
# mock_class.meth1.return_value = 2 # Change this
mock_class_instance = mock_class.return_value # to these
mock_class_instance.meth1.return_value = 2 # two lines
test_parameters = (0, 0)
test_res = myClass(*test_parameters)
self.assertEqual(test_res.param3, 2)
self.assertEqual(test_res.param4, 5)

Related

Mock same Python function across different files in a single mock variable

Let's say I have the following python files:
# source.py
def get_one():
return 1
# file1.py
from source import get_one
def func1():
return get_one()
# file2.py
from source import get_one
def func2():
return get_one()
# script.py
from file1 import func1
from file2 import func2
def main(a, b):
count = 0
for _ in range(a):
count += func1()
for _ in range(b):
count += func2()
return count
I know I can mock out get_one() in main.py using the following setup:
def test_mock():
with (
patch("file1.get_one") as mock1,
patch("file2.get_one") as mock2,
):
main(2, 3)
assert mock1.call_count + mock2.call_count == 5
However, this gets increasingly verbose and hard to read if get_one() needs to be mocked out in many files. I would love to be able to mock out all of its locations in a single mock variable. Something like:
# this test fails, I'm just showing what this ideally would look like
def test_mock():
with patch("file1.get_one", "file2.get_one") as mock:
main(2, 3)
assert mock.call_count == 5
Is there anyway to do this or do I need to use multiple mocks?
Note, I know I can't mock where the function is defined, e.g. patch("source.get_one").
patch accepts new as the object to patch the target with:
def test_mock():
with (
patch("file1.get_one") as mock,
patch("file2.get_one", new=mock),
):
main(2, 3)
assert mock.call_count == 5, mock.call_count
You can write a helper context manager:
import contextlib
from unittest.mock import DEFAULT, patch
#contextlib.contextmanager
def patch_same(target, *targets, new=DEFAULT):
with patch(target, new=new) as mock:
if targets:
with patch_same(*targets, new=mock):
yield mock
else:
yield mock
Usage:
def test_mock():
with patch_same("file1.get_one", "file2.get_one") as mock:
main(2, 3)
assert mock.call_count == 5
A possible workaround: add a level of indirection in source.py so that get_one has a dependency that you can patch:
def get_one():
return _get_one()
def _get_one():
return 1
and now you can do this in your test:
from script import main
from unittest.mock import patch
def test_mock():
with patch("source._get_one") as mock1:
main(2, 3)
assert mock1.call_count == 5

Mocked class and methods not being patched

I have a file, main.py, which contains one method, main():
from grab_api_tokens import GrabApiTokens
def main()
grab_api_tokens = GrabApiTokens(site_one, site_two)
site_one_token = grab_api_tokens.login_to_site_one
site_two_token = grab_api_tokens.login_to_site_two
When trying to test this, I'm using:
import unittest
from unittest.mock import patch, Mock
import main
class TestMain(unittest.TestCase):
#patch('main.grab_apitokens.GrabApiTokens.login_to_site_two')
#patch('main.grab_apitokens.GrabApiTokens.login_to_site_one')
#patch('main.grab_apitokens.GrabApiTokens')
def test_main(self, mock_grab_api_tokens, mock_login_to_site_one, mock_login_to_site_two):
mock_grab_api_tokens = Mock(ok=True)
mock_login_to_site_one.return_value = "fake_token_one"
mock_login_to_site_two.return_value = "fake_token_two"
main.main()
self.assertTrue(mock_grab_api_tokens.called)
the problem is that no matter how I change the #patch, I can't seem to get pycharm to recognize the mocked class. Calling main() just calls the plain main method, its not patching over the values I'm specifying. I can't see where I've gone wrong after reading the unittest documentation.
The target you try to patch is not correct. Besides, you don't need to patch the properties of the GrabApiTokens class. You can patch the GrabApiTokens class and assign the fake data to the properties of its instance directly.
E.g.
main.py:
from grab_api_tokens import GrabApiTokens
def main():
site_one = '123'
site_two = '456'
grab_api_tokens = GrabApiTokens(site_one, site_two)
site_one_token = grab_api_tokens.login_to_site_one
site_two_token = grab_api_tokens.login_to_site_two
print('site_one_token: ', site_one_token)
print('site_two_token: ', site_two_token)
grab_api_tokens.py:
class GrabApiTokens():
def __init__(self, site_one, site_two) -> None:
self.login_to_site_one = site_one
self.login_to_site_two = site_two
test_main.py:
import unittest
from unittest.mock import patch, Mock
import main
class TestMain(unittest.TestCase):
#patch('main.GrabApiTokens')
def test_main(self, mock_grab_api_tokens):
mock_instance = mock_grab_api_tokens.return_value
mock_instance.login_to_site_one = "fake_token_one"
mock_instance.login_to_site_two = "fake_token_two"
main.main()
self.assertTrue(mock_grab_api_tokens.called)
if __name__ == '__main__':
unittest.main()
test result:
⚡ coverage run /Users/dulin/workspace/github.com/mrdulin/python-codelab/src/stackoverflow/66641783/test_main.py && coverage report -m --include="src/*"
site_one_token: fake_token_one
site_two_token: fake_token_two
.
----------------------------------------------------------------------
Ran 1 test in 0.001s
OK
Name Stmts Miss Cover Missing
-----------------------------------------------------------------------------
src/stackoverflow/66641783/grab_api_tokens.py 4 2 50% 3-4
src/stackoverflow/66641783/main.py 9 0 100%
src/stackoverflow/66641783/test_main.py 13 0 100%
-----------------------------------------------------------------------------
TOTAL

Mock inner methods Python2

I'm new to mock and really strugling with it. Mostly in documentation and most of SO pages it shows how to get the mock result_value, but I want to check if the values I'm getting from methods are correct, not the result_value's. Here's the example:
#!/usr/bin/env python
class Example:
def one(self):
return 1
def two(self, one):
print(one + 1) # basically any void method, ex: result = one + 1 and check result value if correct
def main(self):
self.two(self.one())
if __name__ == '__main__':
e = Example()
e.main()
Test:
#!/usr/bin/env python
import unittest
import example
from mock import patch
class Example(unittest.TestCase):
def test_one(self):
self.assertEqual(1, et.one())
def test_two(self):
with patch('example.Example.two'):
self.assertEqual(2, et.two(et.one())) # ..the part I'm stuck
# whick ofc throws AssertionError: 2 != <MagicMock name='two()' id='blablabla'>
def test_main(self):
# unknown part..
if __name__ == '__main__':
et = example.Example()
unittest.main()
How to achieve void method check with unittest?
UPDATE:
so the print I figured out with chepner's help:
def test_twoi3(self):
mock_print = MagicMock()
with patch('sys.stdout', mock_print):
print(2)
expected = call.write('2')
self.assertEqual(mock_print.mock_calls[0], expected)
and for main I'm not quite sure if it is a good solution...:
def test_main(self):
with patch ('example.Example.main') as m:
et.main(et.two(1))
m.assert_called_with(et.two(1))
but I want to check not by passing the methods and values, but if main calls other two methods. How to achieve this?
You don't need to mock anything (directly, anyway); you want to capture standard output and verify that it is what you expect.
from contextlib import redirect_stdout
from io import StringIO
def test_two(self):
stdout = StringIO()
with redirect_stdout(stdout):
et.two(et.one())
self.assertEqual(stdout.getvalue(), "2\n")
Or, you can mock the print and check that it is called with the expected arguments.
def test_two(self):
with patch('__builtin__.print') as p:
et.two(et.one())
p.assert_called_with(2)
I have figured out how to test if main methods have been called. Test looks like this:
#!/usr/bin/env python
import unittest
import example
from mock import patch, MagicMock, call
class Example(unittest.TestCase):
def setUp(self):
self.et = example.Example()
def test_one(self):
self.assertEqual(1, self.et.one())
def test_two(self):
mock_print = MagicMock()
with patch('sys.stdout', mock_print):
print(2)
expected = call.write('2')
self.assertEqual(mock_print.mock_calls[0], expected)
def test_main(self):
self.et.two = MagicMock(side_effect=self.et.two)
self.et.one = MagicMock(side_effect=self.et.one)
self.et.main()
self.et.one.assert_called()
self.et.two.assert_called()
self.et.one.__str__ = self.et.one
self.assertEqual(1, int(self.et.one))
if __name__ == '__main__':
unittest.main()
Upon mocking methods that are in main (all of them) and calling main methods one and two have been successfully called. For one you can return value by using __str__

How to properly mock private members of a class

I am trying to write some unit tests for a method that depends on another private method. - As shown in the example below:
def is_member_of(self, group_name):
members = self.__get_group_members(group_name)
The private method that I'd like to mock is __get_group_members; I'd also like to mock the private attribute __user_id since it will be used in the is_member_of function (not shown in the example above).
What I have so far:
import unittest
from unittest import mock
class Test(unittest.TestCase):
group_data = []
user_id = 'test_user_id'
def mock_dependencies(self, x):
x.__user_id = mock.PropertyMock(return_value=self.user_id)
x.__get_group_members = mock.MagicMock(return_value=self.group_data)
def first_test(self):
x = A(('name', 'group'))
self.mock_dependencies(x)
x.is_member_of('test_group')
When I invoke x.is_member_of() the mocking doesn't work as anticipated.
You can access a private attribute in Python since private and protected are by convention. - What you're looking for is basically using _ClassName__private_attribute_name since, python carries out the renaming in order to achieve the convention agreed upon.
Example (returning a MagicMock):
with mock.patch.object(Class, '_ClassName__private_attribute_name', return_value='value') as obj_mock:
pass
Example (returning a raw value):
with mock.patch.object(Class, '_ClassName__private_attribute_name', new_callable=PropertyMock) as obj_mock:
obj_mock.return_value = 'string value'
Class is a reference to the class itself - not the instance.
Complete Example:
from unittest.mock import patch, PropertyMock
from unittest import TestCase, main
class Private:
__attr = 'hello'
class PrivateTest(TestCase):
#patch.object(Private, '_Private__attr', new_callable=PropertyMock)
def test_private_attribute_value_change_decorator_success(self, private_mock):
obj = Private()
private_mock.return_value = 'string'
self.assertEqual('string', obj._Private__attr)
def test_private_attribute_value_change_context_manager_success(self):
with patch.object(Private, '_Private__attr', new_callable=PropertyMock) as o_mock:
obj = Private()
o_mock.return_value = 'mocked value'
self.assertEqual('mocked value', obj._Private__attr)
if __name__ == '__main__':
main()
Modifications to your example:
from unittest import TestCase, mock, main
class A:
__user_id = 3
def __init__(self, user, group):
"""
Your logic is missing - obviously
:param user:
:param group:
"""
def __get_group_members(self):
"""
Your logic is missing - obviously
:return:
"""
return ['user_1', 'user_2']
def is_member_of(self, group_name):
members = self.__get_group_members(group_name)
# will return if the user is a member of the group
return self.__user_id in members
class GroupTest(TestCase):
group_data = [1, 2]
user_id = 'test_user_id'
#mock.patch.object(A, '_A__get_group_members')
#mock.patch.object(A, '_A__user_id', new_callable=mock.PropertyMock)
def test_this_is_my_first_success(self, user_id_mock: mock.PropertyMock, get_group_members_mock: mock.MagicMock):
get_group_members_mock.return_value = self.group_data
user_id_mock.return_value = 3
x = A('user_3', 'this_group')
self.assertEqual(False, x.is_member_of('test_group'))
#mock.patch.object(A, '_A__get_group_members')
#mock.patch.object(A, '_A__user_id', new_callable=mock.PropertyMock)
def test_this_is_my_first_failure(self, user_id_mock: mock.PropertyMock, get_group_members_mock: mock.MagicMock):
get_group_members_mock.return_value = self.group_data
user_id_mock.return_value = 1
x = A('user_1', 'this_group')
self.assertEqual(True, x.is_member_of('test_group'))
if __name__ == '__main__':
main()
If you know you'll mock these two attributes in all test cases you can add the decorators on the class level and expect the arguments like-wise.
In the case where the attribute is set through the __init__ or any other method, you could simply alter it as shown below.
from unittest import TestCase, mock, main
class A:
def __init__(self, user, group):
"""
Your logic is missing - obviously
:param user:
:param group:
"""
def __get_group_members(self):
"""
Your logic is missing - obviously
:return:
"""
return ['user_1', 'user_2']
def is_member_of(self, group_name):
members = self.__get_group_members(group_name)
# will return if the user is a member of the group
return self.__user_id in members
class GroupTest(TestCase):
group_data = [1, 2]
user_id = 'test_user_id'
#mock.patch.object(A, '_A__get_group_members')
def test_this_is_my_first_success(self, get_group_members_mock: mock.MagicMock):
x = A('user_3', 'this_group')
x._A__user_id = 5
get_group_members_mock.return_value = self.group_data
self.assertEqual(False, x.is_member_of('test_group'))
#mock.patch.object(A, '_A__get_group_members')
def test_this_is_my_first_failure(self, get_group_members_mock: mock.MagicMock):
get_group_members_mock.return_value = self.group_data
x = A('user_1', 'this_group')
x._A__user_id = 1
self.assertEqual(True, x.is_member_of('test_group'))
if __name__ == '__main__':
main()

python mock global function that is used in class

I can't seem to get my head around mocking in Python. I have a global function:
a.py:
def has_permission(args):
ret_val = ...get-true-or-false...
return ret_val
b.py:
class MySerializer(HyperlinkedModelSerializer):
def get_fields():
fields = super().get_fields()
for f in :
if has_permission(...):
ret_val[f.name] = fields[f]
return ret_val
c.py:
class CountrySerializer(MySerializer):
class Meta:
model = Country
Question: Now i want to test c.py, but i want to mock the has_permission function that is defined in a.py, but is called in the get_fields-method of the class MySerializer that is defined in b.py ... How do i do that?
I've tried things like:
#patch('b.MySerializer.has_permission')
and
#patch('b.MySerializer.get_fields.has_permission')
and
#patch('a.has_permission')
But everything i try either just doesn't work and has_permission is still executed, or python complains about that it can't find the attribute 'has_permission'
with the patching done in:
test.py
class TestSerializerFields(TestCase):
#patch(... the above examples....)
def test_my_country_serializer():
s = CountrySerializer()
self..assertTrue(issubclass(my_serializer_fields.MyCharField, type(s.get_fields()['field1'])))
You need to patch the global in the b module:
#patch('b.has_permission')
because that's where your code looks for it.
Also see the Where to patch section of the mock documentation.
You need to patch the method where it exists at the time your test runs. If you try and patch the method where it is defined after the test code has already imported it, then the patch will have no effect. At the point where the #patch(...) executes, the test code under test has already grabbed the global method into its own module.
Here is an example:
app/util/config.py:
# This is the global method we want to mock
def is_search_enabled():
return True
app/service/searcher.py:
# Here is where that global method will be imported
# when this file is first imported
from app.util.config import is_search_enabled
class Searcher:
def __init__(self, api_service):
self._api_service = api_service
def search(self):
if not is_search_enabled():
return None
return self._api_service.perform_request('/search')
test/service/test_searcher.py:
from unittest.mock import patch, Mock
# The next line will cause the imports of `searcher.py` to execute...
from app.service.searcher import Searcher
# At this point, searcher.py has imported is_search_enabled into its module.
# If you later try and patch the method at its definition
# (app.util.config.is_search_enabled), it will have no effect because
# searcher.py won't look there again.
class MockApiService:
pass
class TestSearcher:
# By the time this executes, `is_search_enabled` has already been
# imported into `app.service.searcher`. So that is where we must
# patch it.
#patch('app.service.searcher.is_search_enabled')
def test_no_search_when_disabled(self, mock_is_search_enabled):
mock_is_search_enabled.return_value = False
mock_api_service = MockApiService()
mock_api_service.perform_request = Mock()
searcher = Searcher(mock_api_service)
results = searcher.search()
assert results is None
mock_api_service.perform_request.assert_not_called()
# (For completeness' sake, make sure the code actually works when search is enabled...)
def test_search(self):
mock_api_service = MockApiService()
mock_api_service.perform_request = mock_perform_request = Mock()
searcher = Searcher(mock_api_service)
expected_results = [1, 2, 3]
mock_perform_request.return_value = expected_results
actual_results = searcher.search()
assert actual_results == expected_results
mock_api_service.perform_request.assert_called_once_with('/search')

Categories

Resources