Mocking a function - python

i try to figure how to mock a function in a helper.py that use in several methods for a unit test.
I try with patch, #patch('project.helpers.function_0', new=lambda: True) but didn't work.
How is the correct way to do this?
Thank you.
Update
I have 1 function and 1 decorator that i need to override for all test set.
helpers.py
def myfunction(asd):
# ...
return asd
decorators.py
def mydecorator(func):
#wraps(func)
def _wrapped_func(asd, *args, **kwargs):
# ...
return func(asd, *args, **kwargs)
return _wrapped_func
How i resolved
I want to know how do this with mock, thank you!
test_base.py
import project.decorators
import project.helpers
def myfunction_mock(asd):
# ...
return asd
helpers.myfunction = myfunction_mock
def mydecorator_mock(func):
# ...
decorators.mydecorator = mydecorator_mock

Some key things going on here.
If possible, you should be using unittest.mock (or mock installed via pip).
You are likely patching in the wrong location.
When using mock.patch you probably want to use the new_callable keyword argument, rather than new
Suppose your production code looks something like this....
#production_module.py
from helpers import myfunction
def some_function(a):
result = myfunction(a) + 1
return result
If you wanted to test some_function from your production code, mocking myfunction your test code should patch production_module.myfunction not helpers.myfunction
So your test code may look like this
import mock
from production_module import some_function
def mock_myfunction(*args, **kwargs):
return 1
#mock.patch('production_module.myfunction', new_callable=mock_myfunction)
def test_some_function(mock_func):
result = some_function(1) # call the production function
assert mock_func.called # make sure the mock was actually used
assert result == 2
Another way of using mock.patch is as a context manager. So the patch will only apply in that context.
with mock.patch(...) as mock_func:
some_function(1) # mock_func is called here
some_function(1) # the mock is no longer in place here

Related

Mock class in Python with decorator patch

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.

Pytest, how to use a context manager to allow Mock to be used only for certain code?

I'm trying to use Pytest to test Python code. The code I'm testing isn't a class but is a bunch of functions in the same file that call other functions in the same file. For example advanced_function1 calls basic_function, and advanced_function2 can also call basic_function.
When I file.basic_function = Mock() in a test it isn't scoped to just that test.
I can verify this because when I run the test (test_advanced_function1) that mocks basic_function then test_advanced_function2 does not pass. However if I only run test_advanced_function2 (meaning basic_function never gets mocked in another test) then it works.
To my knowledge the way to correct this is to use a context manager in test_advanced_function1 and mock basic_function as that context manager. I don't know how to do this.
To simplify things and more directly declare intent I've got a test that checks if the function is a Mock object or not.
myfile.py
def basic_function():
return True
def advanced_function1():
return basic_function()
def advanced_function2():
return not basic_function()
test_myfile.py
from unittest.mock import Mock
import myfile
def test_basic_function(monkeypatch):
assert myfile.basic_function() == True
def test_advanced_function1():
myfile.basic_function = Mock(return_value='foo')
assert myfile.basic_function() == 'foo'
def test_advanced_function2():
assert not isinstance(myfile.basic_function, Mock)
So, how do I use a context manager in test_advanced_function1 to mock basic_function?
EDIT: To clarify, I can't assign the mock as a fixture because there are multiple test cases that run in the real code I'm working on and I can't mock basic_function for all the asserts in advanced_function1. Yes I know I should break this massive test apart into smaller tests but I'm just learning the codebase and don't want to change too much before getting all their tests working again.
The mock.patch context manager is seldom used directly in pytest. Instead we use fixtures. The plugin pytest-mock provides a fixture for using the mock.patch API in a more "pytest-thonic" way, like this:
import pytest
import myfile
def test_basic_function():
assert myfile.basic_function() == True
def test_advanced_function1(mocker):
mocker.patch("myfile.basic_function", return_value="foo")
assert myfile.advanced_function1() == 'foo'
def test_advanced_function2():
assert myfile.advanced_function2() == False
One possible solution is to use patch in a context manager.
test_myfile.py
from unittest.mock import patch
from unittest.mock import Mock
import myfile
def test_basic_function(monkeypatch):
assert myfile.basic_function() == True
def test_advanced_function1():
with patch('myfile.basic_function') as basic_function:
basic_function.return_value = 'foo'
assert myfile.basic_function() == 'foo'
def test_advanced_function2():
assert not isinstance(myfile.basic_function, Mock)
But please let me know if there's a more elegant way to do this!

(pytest) Why doesn't property mock work in fixture?

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"

Why would a pytest factory as fixture be used over a factory function?

In the py.test docs it describes declaring factory methods as fixtures, like-so:
#pytest.fixture
def make_foo():
def __make_foo(name):
foo = Foo()
foo.name = name
return foo
return __make_foo
What are the benefits/tradeoffs of doing this over just defining a make_foo function and using that? I don't understand why it is a fixture.
Actually, the most important advantage is being able to use other fixtures, and make the dependency injection of pytest work for you.
The other advantage is allowing you to pass parameters to the factory, which would have to be static in a normal fixture.
Look at this example:
#pytest.fixture
def mocked_server():
with mock.patch('something'):
yield MyServer()
#pytest.fixture
def connected_client(mocked_server):
client = Client()
client.connect_to(mocked_server, local_port=123) # local_port must be static
return client
You could now write a test that gets a connected_client, but you can't change the port.
What if you need a test with multiple clients? You can't either.
If you now write:
#pytest.fixture
def connect_client(mocked_server):
def __connect(local_port):
client = Client()
client.connect_to(mocked_server, local_port)
return client
return __connect
You get to write tests receiving a connect_client factory, and call it to get an initialized client in any port, and how many times you want!
If you have many simple factories then you can simplify their creation with decorator:
def factory_fixture(factory):
#pytest.fixture(scope='session')
def maker():
return factory
maker.__name__ = factory.__name__
return maker
#factory_fixture
def make_stuff(foo, bar):
return 'foo' + str(foo + bar)
this is equivalent of
#pytest.fixture(score='session')
def make_stuff():
def make(foo, bar):
return 'foo' + str(foo + bar)
return
One example might be a session-level fixture, e.g.:
#pytest.fixture(scope="session")
def make_foo():
def __make_foo(name):
foo = Foo()
foo.name = name
return foo
return __make_foo
This way, Pytest will ensure that only one instance of the factory exists for the duration of your tests. This example in particular perhaps doesn't gain much from this, but if the outer function does a lot of processing, such as reading from a file or initialising data structures, then this can save you a lot of time overall.
see below code, this gives ans to your questions..
import pytest
#pytest.fixture
def make_foo():
def __make_foo(name):
print(name)
return __make_foo
def test_a(make_foo):
make_foo('abc')
def test_b(make_foo):
make_foo('def')
The output is shown below ::
tmp3.py::test_a abc
PASSED
tmp3.py::test_b def
PASSED
basically you can pass the arguments in factory fixture and can use according to your requirement.

Testing a function call inside the function using python unittest framework

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)

Categories

Resources