python3 mock.assert_called_once_with on changing list [duplicate] - list

Consider example:
def func_b(a):
print a
def func_a():
a = [-1]
for i in xrange(0, 2):
a[0] = i
func_b(a)
And test function that tries to test func_a and mocks func_b:
import mock
from mock import call
def test_a():
from dataTransform.test import func_a
with mock.patch('dataTransform.test.func_b', autospec=True) as func_b_mock:
func_a()
func_b_mock.assert_has_calls([call(0), call(1)])
After func_a has executed I try to test if func_a made correct calls to func_b, but since in for loop I am mutating list in the end I get:
AssertionError: Calls not found.
Expected: [call(0), call(1)]
Actual: [call([1]), call([1])]

The following works (the importing mock from unittest is a Python 3 thing, and module is where func_a and func_b are):
import mock
from mock import call
import copy
class ModifiedMagicMock(mock.MagicMock):
def _mock_call(_mock_self, *args, **kwargs):
return super(ModifiedMagicMock, _mock_self)._mock_call(*copy.deepcopy(args), **copy.deepcopy(kwargs))
This inherits from MagicMock, and redefines the call behaviour to deepcopy the arguments and keyword arguments.
def test_a():
from module import func_a
with mock.patch('module.func_b', new_callable=ModifiedMagicMock) as func_b_mock:
func_a()
func_b_mock.assert_has_calls([call([0]), call([1])])
You can pass the new class into patch using the new_callable parameter, however it cannot co-exist with autospec. Note that your function calls func_b with a list, so call(0), call(1) has to be changed to call([0]), call([1]). When run by calling test_a, this does nothing (passes).
Now we cannot use both new_callable and autospec because new_callable is a generic factory but in our case is just a MagicMock override. But Autospeccing is a very cool mock's feature, we don't want lose it.
What we need is replace MagicMock by ModifiedMagicMock just for our test: we want avoid to change MagicMock behavior for all tests... could be dangerous. We already have a tool to do it and it is patch, used with the new argument to replace the destination.
In this case we use decorators to avoid too much indentation and make it more readable:
#mock.patch('module.func_b', autospec=True)
#mock.patch("mock.MagicMock", new=ModifiedMagicMock)
def test_a(func_b_mock):
from module import func_a
func_a()
func_b_mock.assert_has_calls([call([0]), call([1])])
Or:
#mock.patch("mock.MagicMock", new=ModifiedMagicMock)
def test_a():
with mock.patch('module.func_b') as func_b_mock:
from module import func_a
func_a()
func_b_mock.assert_has_calls([call([0]), call([1])])

Related

How to mock.patch a dependancy in Python?

Mocking a method that is a dependancy is not working for me. When the method I need to test is calling its dependancy method, the real method is called instead of the mock version of it.
I do have the following files:
myLibrary.py
from src.myOtherLibrary import myOtherLibrary
class myLibrary():
def __init__():
self.myVar = myOtherLibrary() #dependancy
def my_method():
method1 = self.method1()
externalMethod2 self.myVar.method2() #method2 called from the external class myOtherLibrary
return method1 + externalMethod2
def method1():
return "method1 from myLibrary..."
src/myOtherLibrary.py
class myOtherLibrary():
def method2():
return "method2 from myOtherLibrary..."
Finally the Unit Test:
TestMyLibrary.py
import unittest
import mock
from myLibrary import myLibrary
from src.myOtherLibrary import myOtherLibrary
class TestMyLibrary(unittest.TestCase):
#mock.patch('myLibrary.myLibrary.method1') #mocking this works because it's a sibling method from my_method() to test
#mock.patch('src.myOtherLibrary.myOtherLibrary.method2') #this does not work because it's an external class from myLibrary
def test_my_method(my_method1_to_mock, my_method2_to_mock):
my_method1_to_mock.return_value = "something_to_return.."
my_method2_to_mock.return_value = "something_else_to_return.."
myLibraryVar = myLibrary()
result = myLibraryVar.my_method()
print result #I would expect to see this: "something_to_return..something_else_to_return.."
#But it actually prints this: "something_to_return..method2 from myOtherLibrary..."
#So mocking is not working for method2
self.assertEqual('something_to_return..something_else_to_return..', result)
if __name__ == '__main__':
unittest.main()
Perhaps it's important to mention that myLibrary.py and TestMyLibrary.py are in the same folder, but myOtherLibrary.py is under different folder level.
I hope you can help me find what I'm missing here.
Any suggestion will be very well appreciated.
Thanks.
You can't patch like that because the methods you are trying to mock are class methods not functions. So you need to use patch.object.
#mock.patch.object(myLibrary, 'method1')
#mock.patch.object(myOtherLibrary, 'method2')
...

Test Django Mock - check that function/method is called

I want to check that do_a calls do_b. I'm doing like this:
The code:
def do_a(...):
...
do_b(...)
...
The test:
def test_do_a(self):
...
with patch('...do_b', new_callable=do_nothing()) as mock_do_b:
do_a(...)
mock_do_b.assert_called_once_with(...)
And do_nothing:
def do_nothing():
pass
This is working fine but I had to use do_nothing() which I find hacky. Is there a way to make the same test without the extra useless function do_nothing()?
You could use patch as a decorator
#patch('...do_b')
def test_do_a(self, mock_do_b):
do_a(...)
mock_do_b.assert_called_once_with(...)

python mock patch : a method of instance is called?

In python 2.7, I have this function
from slacker import Slacker
def post_message(token, channel, message):
channel = '#{}'.format(channel)
slack = Slacker(token)
slack.chat.post_message(channel, message)
with mock and patch, I can check that the token is used in Slacker class
import unittest
from mock import patch
from slacker_cli import post_message
class TestMessage(unittest.TestCase):
#patch('slacker_cli.Slacker')
def test_post_message_use_token(self, mock_slacker):
token = 'aaa'
channel = 'channel_name'
message = 'message string'
post_message(token, channel, message)
mock_slacker.assert_called_with(token)
how I can check the string use in post_message ?
I try with
mock_slacker.chat.post_message.assert_called_with('#channel_name')
but I get
AssertionError: Expected call: post_message('#channel_name')
Not called
You need to be specific about where the call is taking place. The code is:
slack.chat.post_message
So, as slack is an instance of the mocked class Slacker, you'll need to use return_value to ensure you're talking about that instance:
mock_slacker.return_value.chat.post_message.assert_called_with
You've patched 'slacker_cli.Slacker' so mock_slacker is a patched class. The call itself is taking place on an instance of that patched class. Calling a class returns an instance, hence the use of return_value.

Mock object issue

I am using mock from voidspace and trying to execute some tests using unittest2 and the behaviour is strange. When i use "#patch.object(Test,'asd')" as a patch i get the mock object in the function arguments.
If i use #patch.object(Test,'asd',new_fun) as patch i dont get the one of the parameters.
I am using mock-1.0.1
Here you can see a small sample of code that exemplifies the problem.
I want to try to understand if this issue is a problem with the way i do the patch or if this is a problem in the library.
import unittest2 as unittest
from mock import patch
class Test:
def asd(self, a, b =""):
print "1"
class Test1:
def kk(self, l):
Test().asd("1")
def kk1(self, l):
Test().asd("1","1")
#patch.object(Test,'asd')
class MockClassUT(unittest.TestCase):
def test_stat_process(self, my_mock):
t = Test1()
def test_stat_process1(self, my_mock):
t = Test1()
def test_stat_process2(self, my_mock):
t = Test1()
def new_fun(*args, **kwargs):
print "1"
#patch.object(Test,'asd',new_fun)
class MockClassUT1(unittest.TestCase):
def test_stat_process(self, my_mock):
t = Test1()
t.kk("1")
my_mock.assert_called_with("k")
testloader = unittest.TestLoader()
testnames = testloader.getTestCaseNames(MockClassUT)
suite = unittest.TestSuite()
for name in testnames:
suite.addTest(MockClassUT(name))
testnames = testloader.getTestCaseNames(MockClassUT1)
for name in testnames:
suite.addTest(MockClassUT1(name))
print testnames
unittest.TextTestRunner(verbosity=2).run(suite)
This is expected behaviour. You have mocked it as a class decorator and you've also specified the new function new_fun. As such the mocked object won't be passed to each of the methods in the test class.
This means you can't expect my_mock as parameter there and it also means you can't write assertions using my_mock.
Furthermore, as an aside, your mocked method new_fun doesn't have the same signature as the method you're mocking (asd). The method asd expects self, a, b="" whereas new_fun doesn't have arguments so I expect an issue to come up there as well when the mocked method is called.

How do I redefine functions in python?

I got a function in a certain module that I want to redefine(mock) at runtime for testing purposes. As far as I understand, function definition is nothing more than an assignment in python(the module definition itself is a kind of function being executed). As I said, I wanna do this in the setup of a test case, so the function to be redefined lives in another module. What is the syntax for doing this?
For example, 'module1' is my module and 'func1' is my function, in my testcase I have tried this (no success):
import module1
module1.func1 = lambda x: return True
import module1
import unittest
class MyTest(unittest.TestCase):
def setUp(self):
# Replace othermod.function with our own mock
self.old_func1 = module1.func1
module1.func1 = self.my_new_func1
def tearDown(self):
module1.func1 = self.old_func1
def my_new_func1(self, x):
"""A mock othermod.function just for our tests."""
return True
def test_func1(self):
module1.func1("arg1")
Lots of mocking libraries provide tools for doing this sort of mocking, you should investigate them as you will likely get a good deal of help from them.
import foo
def bar(x):
pass
foo.bar = bar
Just assign a new function or lambda to the old name:
>>> def f(x):
... return x+1
...
>>> f(3)
4
>>> def new_f(x):
... return x-1
...
>>> f = new_f
>>> f(3)
2
It works also when a function is from another module:
### In other.py:
# def f(x):
# return x+1
###
import other
other.f = lambda x: x-1
print other.f(1) # prints 0, not 2
Use redef: http://github.com/joeheyming/redef
import module1
from redef import redef
rd_f1 = redef(module1, 'func1', lambda x: True)
When rd_f1 goes out of scope or is deleted, func1 will go back to being back to normal
If you want to reload into the interpreter file foo.py that you are editing, you can make a simple-to-type function and use execfile(), but I just learned that it doesn't work without the global list of all functions (sadly), unless someone has a better idea:
Somewhere in file foo.py:
def refoo ():
global fooFun1, fooFun2
execfile("foo.py")
In the python interpreter:
refoo() # You now have your latest edits from foo.py