I'm in trouble replacing a python function from a different module with a TestClass
I'm trying to test a part of my code that contains the function in a module; more in details I would like monkey patch this function.
So, the situation is similar to the following:
Function in the module
def function_in_module():
# do some stuff
return 'ok'
Part of my code that I would like testing
from dir_1.dir_2.dir_3.module_name import function_in_module
class ExampleClass():
def __init__(self):
# do some stuff
self.var_x = function_in_module()
# do some stuff again
Test class
from dir_1.dir_2.dir_3 import module_name
class TestClass(TestCase):
de_monkey = {}
mp = None
def setUp(self):
# save original one
self.de_monkey['function_in_module'] = module_name.function_in_module()
if self.mp is None:
self.mp = MP()
def tearDown(self):
# rollback at the end
module_name.function_in_module = self.de_monkey['function_in_module']
def test_string(self):
module_name.function_in_module = self.mp.monkey_function_in_module
test_obj = ExampleClass()
self.assertEqual(test_obj.var_x, 'not ok')
class MP(object):
#staticmethod
def monkey_function_in_module(self):
return 'not ok'
As the assert statement shows, the expected result is 'not ok', but the result is 'ok'.
I have debugged about this and seems that the different way to call the functions is the reason because this monkey patch doesn't work.
In fact, if I try to call the function in ExampleClass in this way
self.var_x = module_name.function_in_module()
works correctly.
What am I missing? maybe it's a banality but it's driving me crazy
Thank you in advance
Your code under test imports function_in_module and references it directly. Changing the value of module_name.function_in_module has no effect on the code.
You should replace the function directly in the module that contains the code under test, not in the source module.
Note that your life would be easier if you used the mock library, although the question of where to patch would still be the same.
Related
I would like to patch a class in Python in unit testing. The main code is this (mymath.py):
class MyMath:
def my_add(self, a, b):
return a + b
def add_three_and_two():
my_math = MyMath()
return my_math.my_add(3, 2)
The test class is this:
import unittest
from unittest.mock import patch
import mymath
class TestMyMath(unittest.TestCase):
#patch('mymath.MyMath')
def test_add_three_and_two(self, mymath_mock):
mymath_mock.my_add.return_value = 5
result = mymath.add_three_and_two()
mymath_mock.my_add.assert_called_once_with(3, 2)
self.assertEqual(5, result)
unittest.main()
I am getting the following error:
AssertionError: Expected 'my_add' to be called once. Called 0 times.
The last assert would also fail:
AssertionError: 5 != <MagicMock name='MyMath().my_add()' id='3006283127328'>
I would expect that the above test passes. What I did wrong?
UPDATE:
Restrictions:
I would not change the tested part if possible. (I am curious if it is even possible, and this is the point of the question.)
If not possible, then I want the least amount of change in the to be tested part. Especially I want to keep the my_add() function non-static.
Instead of patching the entire class, just patch the function.
class TestMyMath(unittest.TestCase):
#patch.object(mymath.MyMath, 'my_add')
def test_add_three_and_two(self, m):
m.return_value = 5
result = mymath.add_three_and_two()
m.assert_called_once_with(3, 2)
self.assertEqual(5, result)
I think the original problem is that my_math.my_add produces a new mock object every time it is used; you configured one Mock's return_value attribute, but then checked if another Mock instance was called. At the very least, using patch.object ensures you are disturbing your original code as little as possible.
Your code is almost there, some small changes and you'll be okay:
my_add should be a class method since self does not really play a role here.
If my_add is an instance method, then it will be harder to trace the calls, since your test will track the instance signature, not the class sig
Since you are are patching, not stubbing, you should use the "real thing", except when mocking the return value.
Here's what that looks like in your code:
class MyMath:
#classmethod
def my_add(cls, a, b):
return a + b
def add_three_and_two():
return MyMath.my_add(3, 2)
Now, the test:
import unittest
from unittest.mock import patch, MagicMock
import mymath
class TestMyMath(unittest.TestCase):
#patch('mymath.MyMath')
def test_add_three_and_two(self, mymath_mock):
# Mock what `mymath` would return
mymath_mock.my_add.return_value = 5
# We are patching, not stubbing, so use the real thing
result = mymath.add_three_and_two()
mymath.MyMath.my_add.assert_called_once_with(3, 2)
self.assertEqual(5, result)
unittest.main()
This should now work.
For instance, every time a test finds
database.db.session.using_bind("reader")
I want to remove the using_bind("reader")) and just work with
database.db.session
using mocker
Tried to use it like this in conftest.py
#pytest.fixture(scope='function')
def session(mocker):
mocker.patch('store.database.db.session.using_bind', return_value=_db.db.session)
But nothing has worked so far.
Code under test:
from store import database
results = database.db.session.using_bind("reader").query(database.Order.id).join(database.Shop).filter(database.Shop.deleted == False).all(),
and I get
AttributeError: 'scoped_session' object has no attribute 'using_bind' as an error.
Let's start with an MRE where the code under test uses a fake database:
from unittest.mock import Mock, patch
class Session:
def using_bind(self, bind):
raise NotImplementedError(f"Can't bind {bind}")
def query(self):
return "success!"
database = Mock()
database.db.session = Session()
def code_under_test():
return database.db.session.using_bind("reader").query()
def test():
assert code_under_test() == "success!"
Running this test fails with:
E NotImplementedError: Can't bind reader
So we want to mock session.using_bind in code_under_test so that it returns session -- that will make our test pass.
We do that using patch, like so:
#patch("test.database.db.session.using_bind")
def test(mock_bind):
mock_bind.return_value = database.db.session
assert code_under_test() == "success!"
Note that my code is in a file called test.py, so my patch call applies to the test module -- you will need to adjust this to point to the module under test in your own code.
Note also that I need to set up my mock before calling the code under test.
TL;DR
How can I patch or mock "any functions that are not being called/used directly"?
Sceneario
I have a simple unit-test snippet as
# utils/functions.py
def get_user_agents():
# sends requests to a private network and pulls data
return pulled_data
# my_module/tasks.py
def create_foo():
from utils.functions import get_user_agents
value = get_user_agents()
# do something with value
return some_value
# test.py
class TestFooKlass(unittest.TestCase):
def setUp(self):
create_foo()
def test_foo(self):
...
Here in setUp() method I am calling get_user_agents() function indirectly by calling create_foo(). During this execution I have got socket.timeout exception since get_user_agents() tried to access a private network. So, how can I manipulate the return data or the entire get_user_agents function during the test?
Also, is there any way to persists this mock/patch during the whole test suite execution?
It does not matter that you call the function indirectly - important is to patch it as it is imported. In your example you import the function to be patched locally inside the tested function, so it will only be imported at function run time. In this case you have to patch the function as imported from its module (e.g. 'utils.functions.get_user_agents'):
class TestFooKlass(unittest.TestCase):
def setUp(self):
self.patcher = mock.patch('utils.functions.get_user_agents',
return_value='foo') # whatever it shall return in the test
self.patcher.start() # this returns the patched object, i case you need it
create_foo()
def tearDown(self):
self.patcher.stop()
def test_foo(self):
...
If you had imported the function instead at module level like:
from utils.functions import get_user_agents
def create_foo():
value = get_user_agents()
...
you should have patched the imported instance instead:
self.patcher = mock.patch('my_module.tasks.get_user_agents',
return_value='foo')
As for patching the module for all tests: you can start patching in setUp as shown above, and stop it in tearDown.
I have a class with some properties. In my test, I need to set up a fixture, and have the properties mocked. However, the patch only works in the fixture function, not when the fixture is called. Any idea how to fix this?
Here is the simplified version of the problem. Let's assume that this is my class Panda:
class Panda(object):
def __init__(self, name):
self.panda_name = name
#property
def name(self):
return self.panda_name
and this is my test
import pytest
from unittest.mock import patch, PropertyMock
from tmp import Panda
#pytest.fixture
#patch(
'tmp.Panda.name',
new_callable=PropertyMock,
return_value="yuanyuan")
def fixture_panda(mk_name):
p = Panda("this name should not matter")
print(p.name) # print "yuanyuan"
return p
def test_panda_fixture(fixture_panda):
p = fixture_panda
print(p.name) # print "this name should not matter"
# this test fails
assert p.name == "yuanyuan"
The first print function in fixture_panda would print yuanyuan, which means the propertyMock works as expected. However the 2nd print function in test_panda_fixture print this name should not matter, which means the propertyMock doesn't work here. Any idea why this happens and how to fix this?
If you want to monkeypatch something in pytest, you can use their built-in fixture monkeypatch, which can be inserted into all fixtures with scope = function. Here is an example from my codebase:
#pytest.fixture(scope="function", autouse=True)
def no_jwt(monkeypatch):
"""Monkeypatch the JWT verification functions for tests"""
monkeypatch.setattr("flask_jwt_extended.verify_jwt_in_request", lambda: print("Verify"))
If I apply it to your example, I think something like this should work:
#pytest.fixture
def fixture_panda(monkeypatch, mk_name):
monkeypatch.setattr('tmp.Panda.name', "yuanyuan")
p = Panda("this name should not matter")
print(p.name) # print "yuanyuan"
return p
You have three problems. Firstly, you're patching the fixture function but you should be patching the test function. This is because the way you've written it, the assertion falls outside the scope of the patching.
Secondly you should ditch the superfluous mkname.
Thirdly, your return_value is in the wrong place; it needs to apply to the PropertyMock object which the patch returns, not as a parameter to the patching function. When using new_callable you need to set it within the test setup, e.g.:
#patch('tmp.Panda.name', new_callable=PropertyMock)
def test_panda_fixture(mock_name, fixture_panda):
mock_name.return_value = "yuanyuan"
...
However you can do it within the decorator by using new instead of new_callable. Here's a working version which shows that approach:
import pytest
from unittest.mock import patch, PropertyMock
from tmp import Panda
#pytest.fixture
def fixture_panda():
p = Panda("this name should not matter")
print(p.name) # print "yuanyuan"
return p
#patch('tmp.Panda.name', new=PropertyMock(return_value="yuanyuan"))
def test_panda_fixture(fixture_panda):
p = fixture_panda
print(p.name) # print "this name should not matter"
# this test fails
assert p.name == "yuanyuan"
I want to test this class using python unittest framework and also mockito.
class ISightRequestEngine(object):
def __init__(self, pInputString=None):
self.__params = (pInputString)
def openHTTPConnection(self):
pass
def __closeHTTPConnection(self):
pass
def testFunc(self):
print 'test function called'
def startEngine(self):
self.__params.parseinputString()
self.openHTTPConnection()
self.testFunc()
def processRequest(self, header = None):
pass
I wanted to test that function startEngine() calls testFunc().
Similar to what we do in our mocked class,
obj = mock(ISightRequestEngine)
obj.startEngine()
try:
verify(obj).startEngine()
except VerificationError:
Unfortunately this only verifies whether the startEngine function is called or not, but it does not give the actual function call and I cannot verify that whether the call to testFunc() has been made or not.
Is there any way to test this scenario?
I am new to testing world and framework.
In your example you are testing your mock.
You create a mock of ISightRequestingEngine
You call startEngine() method of that mock
You verify that the mocked object was called
What you want to do is:
Mock out testFunc()
Call startEngine()
Verify that testFunc() was called
I'm not familiar with mockito, but what from what I can make up from the documentation, I think you have to do something like the following:
from mockito import mock, verify
# Setup ---------------------------------------------
my_mock = mock(ISightRequestingEngine)
system_under_test = ISightRequestingEngine()
system_under_test.testFunc = my_mock.testfunc # Mock out only testFunc()
# Exercise ------------------------------------------
system_under_test.startEngine()
# Verify --------------------------------------------
verify(my_mock).testFunc()
Having similar such issue, where I am bit lost in writing the test case
class UserCompanyRateLimitValidation:
def __init__(self, user_public_key):
self.adapter = UserAdapter(user_public_key)
container = self.adapter.get_user_company_rate_limit()
super(UserCompanyRateLimitValidation, self).__init__(container,\
UserCompanyRateLimitValidation.TYPE)
I have to test this function. I have written test case something like this. I have tried to mock the UserAdapter class but I am not able to do so completely.
def test_case_1():
self.user_public_key = 'TEST_USER_PUBLIC_KEY_XXXXXX1234567890XXXXX'
UserAdapter_mock = mock(UserAdapter)
when(UserAdapter_mock).get_user_company_rate_limit().\
thenReturn(self.get_container_object())
self.test_obj = UserCompanyRateLimitValidation(self.user_public_key)
Here if you see I have mocked get_user_company_rate_limit() call from the testable function, container = self.adapter.get_user_company_rate_limit()
but I am still not able to figure out the way in which I can mock this call,
self.adapter = UserAdapter(user_public_key)