Partial patch in Python with mock - python

How can I use mock.patch on a function, so I have an access to methods .assert_called etc. and simultaneously I can still preserve original functionality of the functions?
Here is example code:
from unittest import mock
def foo(arg):
print(arg)
def tested():
foo('hi')
#mock.patch('__main__.foo')
def test(foo):
tested()
foo.assert_called_once()
test()
I want it to test if the foo function was called just once but I still need it to print hi.

Oh. I have it solved already. I'v just needed to add parameter side_effect to the decorator :-)
Like this:
#mock.patch('__main__.foo', side_effect=foo)
def test(foo):
...

Related

python unittest.mock.patch strange behaviour

I use unittest in my testing. I import a class in yhab.main package as...
from yhab.blah import SomeClass
def some_func():
some_instance = SomeClass()
return some_instance.method()
yhab.blah.SomeClass is defined as...
class SomeClass:
def method(self):
return 'hello'
And then I write a test like this...
#mock.patch('yhab.blah.SomeClass')
def test_mock_of_blah_someclass(mock_some_class):
assert some_func() != 'hello'
the invocation of method() calls the real instance, not a mock.
But if I do this...
#mock.patch('yhab.main.SomeClass')
def test_mock_of_main_someclass(mock_some_class):
assert some_func() != 'hello'
the invocation of method() calls the mock, not the real instance and the test passes.
Why is that?
I was thinking that python must make some sort of copy of the class definition when an import happens, but I wrote a test that proves that to not be the case.
The docs say the following, which kind of eludes to this, but it doesn't really say it outright, and IMO doesn't really explain it, especially for a python newb...
Patching a class replaces the class with a MagicMock instance. If the
class is instantiated in the code under test then it will be the
return_value of the mock that will be used.
Do the docs need to be updated to be clear?
After you import SomeClass from yhab.blah, it ends up in the yhab.main namespace, not in the yhab.blah namespace.
Try to use #mock.patch('yhab.main.SomeClass') instead of #mock.patch('yhab.blah.SomeClass').

How to determine if method is called using Python mock but does not replace the function body?

There are plenty of examples that shows how to assert a method has been called using Mock, eg. assert_called_with(), but all of them involve replacing the method with Mock instance.
What I want is a little bit different, I want the function to be executed normally without its body replaced, but still want to assert if the function has been called.
eg.
def dosomething(...)
# creates records that I will test later on.
....
def execute():
....
dosomething()
in my tests
def test_a(...):
with patch(dosomething...) as mocked:
execute()
mocked.assert_called_with()
I know I can test against the records that dosomething() creates instead. Yes I agree, but I just want to find out if it's possible to do per as my question.
Use Mock's wraps kwarg and pass it the original method.
For example,
>>> from unittest import mock
>>> def hi(name): print('hi', name)
>>> mock_hi = mock.Mock(wraps=hi)
The wrapped function is called by the mock.
>>> mock_hi('Bob')
hi Bob
But it's still a mock that remembers calls.
>>> mock_hi.call_args_list
[call('Bob')]
Recall that patch() will pass along extra kwargs to the Mock it makes, so you can use the wraps argument here too. For example,
>>> with mock.patch('builtins.float', wraps=float) as mock_float:
... x = float('inf')
... print(x) # If we hadn't wrapped, x would be a mock.
... print(mock_float.call_args_list)
...
inf
[call('inf')]

using self in python #patch decorator

I'm trying to use python's mock.patch to implement unit tests with nose.
class A:
def setUp(self):
self.b = 8 #contrived example
#patch.object('module.class', 'function', lambda x: self.b)
def testOne(self):
# do test #
Here, patch complains that it doesnt know self (which is correct). What is best way to get this kind of functionality in a clean fashion?
I know I can use a global variable, or that I can mock it within the test (but that involves me cleaning up the objects at the end of the test).
You cannot use self on method decorator because you are in the class definition and the object doesn't exist. If you really want to access to self and not just use some static values you can consider follow approach: totest is a module in my python path and fn is the method that I would patch, moreover I'm using a fixed return_value instead a function for a more readable example
class MyTestCase(unittest.TestCase):
def setUp(self):
self.b = 8 #contrived example
def testOne(self):
with patch('totest.fn', return_value=self.b) as m:
self.assertEqual(self.b, m())
self.assertTrue(m.called)
#patch("totest.fn")
def testTwo(self,m):
m.return_value = self.b
self.assertEqual(self.b, m())
self.assertTrue(m.called)
In testOne() I use patch as a context and I will have the full access to self. In testTwo() (that is my standard way) I set up my mock m at the start of the test and then use it.
Finally I used patch() instead of patch.object() because I don't really understand why you need patch.object() but you can change it as you like.

#Patch decorator is not compatible with pytest fixture

I have encountered something mysterious, when using patch decorator from mock package integrated with pytest fixture.
I have two modules:
-----test folder
-------func.py
-------test_test.py
in func.py:
def a():
return 1
def b():
return a()
in test_test.py:
import pytest
from func import a,b
from mock import patch,Mock
#pytest.fixture(scope="module")
def brands():
return 1
mock_b=Mock()
#patch('test_test.b',mock_b)
def test_compute_scores(brands):
a()
It seems that patch decorate is not compatible with pytest fixture. Does anyone have a insight on that? Thanks
When using pytest fixture with mock.patch, test parameter order is crucial.
If you place a fixture parameter before a mocked one:
from unittest import mock
#mock.patch('my.module.my.class')
def test_my_code(my_fixture, mocked_class):
then the mock object will be in my_fixture and mocked_class will be search as a fixture:
fixture 'mocked_class' not found
But, if you reverse the order, placing the fixture parameter at the end:
from unittest import mock
#mock.patch('my.module.my.class')
def test_my_code(mocked_class, my_fixture):
then all will be fine.
As of Python3.3, the mock module has been pulled into the unittest library. There is also a backport (for previous versions of Python) available as the standalone library mock.
Combining these 2 libraries within the same test-suite yields the above-mentioned error:
E fixture 'fixture_name' not found
Within your test-suite's virtual environment, run pip uninstall mock, and make sure you aren't using the backported library alongside the core unittest library. When you re-run your tests after uninstalling, you would see ImportErrors if this were the case.
Replace all instances of this import with from unittest.mock import <stuff>.
Hopefully this answer on an old question will help someone.
First off, the question doesn't include the error, so we don't really know what's up. But I'll try to provide something that helped me.
If you want a test decorated with a patched object, then in order for it to work with pytest you could just do this:
#mock.patch('mocked.module')
def test_me(*args):
mocked_module = args[0]
Or for multiple patches:
#mock.patch('mocked.module1')
#mock.patch('mocked.module')
def test_me(*args):
mocked_module1, mocked_module2 = args
pytest is looking for the names of the fixtures to look up in the test function/method. Providing the *args argument gives us a good workaround the lookup phase. So, to include a fixture with patches, you could do this:
# from question
#pytest.fixture(scope="module")
def brands():
return 1
#mock.patch('mocked.module1')
def test_me(brands, *args):
mocked_module1 = args[0]
This worked for me running python 3.6 and pytest 3.0.6.
If you have multiple patches to be applied, order they are injected is important:
# from question
#pytest.fixture(scope="module")
def brands():
return 1
# notice the order
#patch('my.module.my.class1')
#patch('my.module.my.class2')
def test_list_instance_elb_tg(mocked_class2, mocked_class1, brands):
pass
This doesn't address your question directly, but there is the pytest-mock plugin which allows you to write this instead:
def test_compute_scores(brands, mock):
mock_b = mock.patch('test_test.b')
a()
a) For me the solution was to use a with block inside the test function instead of using a #patch decoration before the test function:
class TestFoo:
def test_baa(self, my_fixture):
with patch(
'module.Class.function_to_patch',
MagicMock(return_value='mocked_result')
) as mocked_function_to_patch:
result= my_fixture.baa('mocked_input')
assert result == 'mocked_result'
mocked_function_to_patch.assert_has_calls([
call('mocked_input')
])
This solution does work inside classes (that are used to structure/group my test methods). Using the with block, you don't need to worry about the order of the arguments. I find it more explicit then the injection mechanism but the code becomes ugly if you patch more then one variable. If you need to patch many dependencies, that might be a signal that your tested function does too many things and that you should refactor it, e.g. by extracting some of the functionality to extra functions.
b) If you are outside classes and do want a patched object to be injected as extra argument in a test method... please note that #patch does not support to define the mock as second argument of the decoration:
#patch('path.to.foo', MagicMock(return_value='foo_value'))
def test_baa(self, my_fixture, mocked_foo):
does not work.
=> Make sure to pass the path as only argument to the decoration. Then define the return value inside the test function:
#patch('path.to.foo')
def test_baa(self, my_fixture, mocked_foo):
mocked_foo.return_value = 'foo_value'
(Unfortunately, this does not seem to work inside classes.)
First let inject the fixture(s), then let inject the variables of the #patch decorations (e.g. 'mocked_foo').
The name of the injected fixture 'my_fixture' needs to be correct. It needs to match the name of the decorated fixture function (or the explicit name used in the fixture decoration).
The name of the injected patch variable 'mocked_foo' does not follow a distinct naming pattern. You can choose it as you like, independent from the corresponding path of the #patch decoration.
If you inject several patched variables, note that the order is reversed: the mocked instance belonging to the last #patch decoration is injected first:
#patch('path.to.foo')
#patch('path.to.qux')
def test_baa(self, my_fixture, mocked_qux, mocked_foo):
mocked_foo.return_value = 'foo_value'
I had the same problem and solution for me was to use mock library in 1.0.1 version (before I was using unittest.mock in 2.6.0 version). Now it works like a charm :)

Nose ignores test with custom decorator

I have some relatively complex integration tests in my Python code. I simplified them greatly with a custom decorator and I'm really happy with the result. Here's a simple example of what my decorator looks like:
def specialTest(fn):
def wrapTest(self):
#do some some important stuff
pass
return wrapTest
Here's what a test may look like:
class Test_special_stuff(unittest.TestCase):
#specialTest
def test_something_special(self):
pass
This works great and is executed by PyCharm's test runner without a problem. However, when I run a test from the commandline using Nose, it skips any test with the #specialTest decorator.
I have tried to name the decorator as testSpecial, so it matches default rules, but then my FN parameter doesn't get passed.
How can I get Nose to execute those test methods and treat the decorator as it is intended?
SOLUTION
Thanks to madjar, I got this working by restructuring my code to look like this, using functools.wraps and changing the name of the wrapper:
from functools import wraps
def specialTest(fn):
#wraps(fn)
def test_wrapper(self,*args,**kwargs):
#do some some important stuff
pass
return test_wrapper
class Test_special_stuff(unittest.TestCase):
#specialTest
def test_something_special(self):
pass
If I remember correctly, nose loads the test based on their names (functions whose name begins with test_). In the snippet you posted, you do not copy the __name__ attribute of the function in your wrapper function, so the name of the function returned is wrapTest and nose decides it's not a test.
An easy way to copy the attributes of the function to the new one is to used functools.wraps.

Categories