Why can't mock.call() accept self as a kwarg? - python

Background
In python mocking, there is a function assert_has_calls which can verify the order of arguments used to call a function. It has a helper object call which helps validate the arguments.
Problem
Using call(), you cannot pass self as a kwarg. It gives the error: TypeError: __call__() got multiple values for argument 'self'. Why? The workaround is to simply not use self as a kwarg, but I'm afraid there's something fundamental I'm misunderstanding and can't find anything in the documentation that points to it.
Live Code
The code below is pasted here for you to play with: https://repl.it/#artoonie/ObviousIntentReality#main.py
Pasted Code
File 1: test_class.py
needs to be a separate file for mock.patch decorator discoverability
class Class:
def func(self, num):
return num
class Runner:
def run(self):
return Class().func(1)
File 2: test.py
import mock
import test_class
unmockedFunction = test_class.Class.func
class Test:
#mock.patch('test_class.Class.func', autospec=True)
def test_me(self, mocker):
# Make the mock do nothing
mocker.side_effect = unmockedFunction
# Run the function
test_class.Runner().run()
# This works
mocker.assert_called_with(self=mock.ANY, num=1)
# This works
mocker.assert_has_calls([
mock.call(mock.ANY, num=1)
])
# This fails
mocker.assert_has_calls([
mock.call(self=mock.ANY, num=1)
])
Test().test_me()

Related

Test instance of class that mocked method is called from in Python

I am mocking out a method of a class and want to test the instance of the class that the method was called from to test that the creation part of my function works as expected.
In my particular case do_stuff tries to write bar_instance to an Excel File and I don't want that to happen i.e.
def create_instance(*args):
return Bar(*args)
class Bar():
def __init__(self, *args):
self.args = args
def do_stuff(self):
pass
def foo(*args):
bar_instance = create_instance(*args)
bar_instance.do_stuff()
Then in a testing file
from unittest import TestCase
from unittest.mock import patch
from path.to.file import foo
class TestFoo(TestCase):
#patch('path.to.file.Bar.do_stuff')
def test_foo(self, mock_do_stuff):
test_args = [1]
_ = foo(*test_args)
# Test here the instance of `Bar` that `mock_do_stuff` was called from
# Something like
actual_args = list(bar_instance.args)
self.assertEqual(test_args, actual_args)
I put a break in the test function after foo(*test_args) is run but can't see any way from the mocked method of accessing the instance of Bar it was called from and am a bit stuck. I don't want to mock out Bar further up the code as I want to make sure the correct instance of Bar is being created.
In your code example, there are three things that might need testing: function create_instance, class Bar and function foo. I understand your test code such that you want to ensure that function foo calls do_stuff on the instance returned by create_instance.
Since the original create_instance function has control over the created instance, a solution of your problem is to mock create_instance such that your test gains control of the object that is handed over to foo:
import unittest
from unittest import TestCase
from unittest.mock import patch, MagicMock
from SO_60624698 import foo
class TestFoo(TestCase):
#patch('SO_60624698.create_instance')
def test_foo_calls_do_stuff_on_proper_instance (
self, create_instance_mock ):
# Setup
Bar_mock = MagicMock()
create_instance_mock.return_value = Bar_mock
# Exercise
foo(1, 2, 3) # args are irrelevant
# Verify
Bar_mock.do_stuff.assert_called()
if __name__ == '__main__':
unittest.main()
In addition, you might also want to test if foo passes the arguments correctly to create_instance. This could be implemented as a separate test:
...
#patch('SO_60624698.create_instance')
def test_foo_passes_arguments_to_create_instance (
self, create_instance_mock ):
# Setup
create_instance_mock.return_value = MagicMock()
# Exercise
foo(1, 22, 333)
# Verify
create_instance_mock.assert_called_with(1, 22, 333)
And, certainly, to complete the whole test of the object generation, you could test create_instance directly, by calling it and checking on the returned instance of Bar if it has used its arguments correctly for the construction of the Bar instance.
As patch returns an instance of Mock (or actually MagicMock, but it inherits the relevant methods from its base - Mock), you have the assert_called_with method available, which should do the trick.
Note that this method is sensitive to args/kwargs - you have to assert the exact same call.
Another note: it might be a better practice to use patch.object instead of patch here

timeout decorator in python through __init__ params

I want to have a timeout for a function (rest API call) in python & for that I am following this SO answer.
I already have one existing code structure where i want to have a timeout decorator. I am defining a timeout seconds in ".txt" file & passing it as a dict to main function. Something like :
class Foo():
def __init__(self, params):
self.timeout=params.get['timeout']
....
....
#timeout(self.timeout) #throws an error
def bar(arg1,arg2,arg3,argn):
pass
#handle timeout for this function by getting timeout from __init__
#As answer given in SO ,
#I need to use the self.timeout, which is throwing an error:
***signal.alarm(seconds)
TypeError: an integer is required***
#And also
***#timeout(self.timeout)
NameError: name 'self' is not defined***
#timeout(30) #Works fine without any issue
def foo_bar(arg1,arg2,arg3,argn):
pass
What am i missing ?
At A the self is not defined because the decorator is outside the method, but self only exists inside the method:
#timeout(self.timeout) # <== A
def bar(self,arg1,arg2,arg3):
pass
You can try to set the barTimeout attribute at __init__:
class Foo():
def bar(self,arg1,arg2,arg3):
pass
def __init__(self, params):
self.timeout=params.get('timeout')
self.barTimeout = timeout(self.timeout)(self.bar)
Foo({'timeout':30}).barTimeout(1,2,3)

Why does unittest.mock fail when the production class constructor takes extra arguments?

I have run into a problem which I think might be a bug with the libraries I am using. However, I am fairly new to python, unittest, and unittest.mock libraries so this may just be a hole in my understanding.
While adding tests to some production code I have run into an error, I have produced a minimal sample that reproduces the issue:
import unittest
import mock
class noCtorArg:
def __init__(self):
pass
def okFunc(self):
raise NotImplemented
class withCtorArg:
def __init__(self,obj):
pass
def notOkFunc(self):
raise NotImplemented
def okWithArgFunc(self, anArgForMe):
raise NotImplemented
class BasicTestSuite(unittest.TestCase):
"""Basic test Cases."""
# passes
def test_noCtorArg_okFunc(self):
mockSUT = mock.MagicMock(spec=noCtorArg)
mockSUT.okFunc()
mockSUT.assert_has_calls([mock.call.okFunc()])
# passes
def test_withCtorArg_okWithArgFuncTest(self):
mockSUT = mock.MagicMock(spec=withCtorArg)
mockSUT.okWithArgFunc("testing")
mockSUT.assert_has_calls([mock.call.okWithArgFunc("testing")])
# fails
def test_withCtorArg_doNotOkFuncTest(self):
mockSUT = mock.MagicMock(spec=withCtorArg)
mockSUT.notOkFunc()
mockSUT.assert_has_calls([mock.call.notOkFunc()])
if __name__ == '__main__':
unittest.main()
How I run the tests and the output is as follows:
E:\work>python -m unittest testCopyFuncWithMock
.F.
======================================================================
FAIL: test_withCtorArg_doNotOkFuncTest (testCopyFuncWithMock.BasicTestSuite)
----------------------------------------------------------------------
Traceback (most recent call last):
File "testCopyFuncWithMock.py", line 38, in test_withCtorArg_doNotOkFuncTest
mockSUT.assert_has_calls([mock.call.notOkFunc()])
File "C:\Python27\lib\site-packages\mock\mock.py", line 969, in assert_has_calls
), cause)
File "C:\Python27\lib\site-packages\six.py", line 718, in raise_from
raise value
AssertionError: Calls not found.
Expected: [call.notOkFunc()]
Actual: [call.notOkFunc()]
----------------------------------------------------------------------
Ran 3 tests in 0.004s
FAILED (failures=1)
I am using python 2.7.11, with mock version 2.0.0 installed via pip.
Any suggestions for what I am doing wrong? Or does this look like a bug in the library?
Interestingly, the way you chose to perform the assert has masked your issue.
Try, instead of this:
mockSUT.assert_has_calls(calls=[mock.call.notOkFunc()])
to do this:
mockSUT.assert_has_calls(calls=[mock.call.notOkFunc()], any_order=True)
You'll see the actual exception:
TypeError("'obj' parameter lacking default value")
This is because you tried to instantiate an instance of the class withCtorArg that has the parameter obj with no default value. If you had tried to actually instantiate it directly, you would've seen:
TypeError: __init__() takes exactly 2 arguments (1 given)
However, since you let the mock library handle the instantiation of a mock object, the error happens there - and you get the TypeError exception.
Modifying the relevant class:
class withCtorArg:
def __init__(self, obj = None):
pass
def notOkFunc(self):
pass
def okWithArgFunc(self, anArgForMe):
pass
and adding a default None value for obj solves the issue.
I don't think I can explain definitively why this is the case, I still suspect a bug in the Mock library, since the issue only occurs for a test case with no arguments on the called function. Thanks to advance512 for pointing out that the real error was hidden!
However to work around this issue, without having to modify the production code I am going to use the following approach:
# passes
#mock.patch ('mymodule.noCtorArg')
def test_noCtorArg_okFunc(self, noCtorArgMock):
mockSUT = noCtorArg.return_value
mockSUT.okFunc()
mockSUT.assert_has_calls([mock.call.okFunc()])
# passes
#mock.patch ('mymodule.withCtorArg')
def test_withCtorArg_okWithArgFuncTest(self, withCtorArgMock):
mockSUT = withCtorArg.return_value
mockSUT.okWithArgFunc("testing")
mockSUT.assert_has_calls([mock.call.okWithArgFunc("testing")])
# now passes
#mock.patch ('mymodule.withCtorArg')
def test_withCtorArg_doNotOkFuncTest(self, withCtorArgMock):
mockSUT = withCtorArg.return_value
mockSUT.notOkFunc()
mockSUT.assert_has_calls([mock.call.notOkFunc()], any_order=True)
Edit:
One problem with this is that the mock does not have spec set. This means that the SUT is able to call methods that do not exist on the original class definition.
An alternative approach is to wrap the class to be mocked:
class withCtorArg:
def __init__(self,obj):
pass
def notOkFunc(self):
raise NotImplemented
def okWithArgFunc(self, anArgForMe):
raise NotImplemented
class wrapped_withCtorArg(withCtorArg):
def __init__(self):
super(None)
class BasicTestSuite(unittest.TestCase):
"""Basic test Cases."""
# now passes
def test_withCtorArg_doNotOkFuncTest(self):
mockSUT = mock.MagicMock(spec=wrapped_withCtorArg)
mockSUT.notOkFunc()
#mockSUT.doesntExist() #causes the test to fail. "Mock object has no attribute 'doesntExist'"
assert isinstance (mockSUT, withCtorArg)
mockSUT.assert_has_calls([mock.call.notOkFunc()], any_order=True)

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)

Python Decorator Self-firing

I am fairly new to Python and have been learning about decorators. After messing around with Flask, I am trying to write some code that simulates their route handler/decorators, just to understand how decorators (with arguments) work.
In the code below, the route decorator seems to call itself once the script runs. My question is, how is it possible that app.route() gets called when i run this script, and what is really happening here? Notice i don't call my index() function anywhere directly.
# test.py
class Flask(object):
def __init__(self, name):
self.scriptname = name
def route(self, *rargs, **kargs):
args = list(rargs)
if kargs:
print(kargs['methods'])
def decorator(f):
f(args[0])
return decorator
app = Flask(__name__)
#app.route("/", methods = ["GET","PUT"])
def index(rt):
print('route: ' + rt)
the above prints this in my terminal:
$ python test.py
['GET', 'PUT']
route: /
Any insight would be appreciated.
#app.route("/", methods = ["GET","PUT"]) is an executable statement: it calls the route() method of the app object. Since it's at module level, it will be executed when the script is imported.
Now, the result of calling app.route(...) is a function, and because you've used the # to mark it as a decorator, that function will wrap index. Note that the syntax is just a shortcut for this:
index = app.route(...)(index)
in other words, Python will call the function returned by app.route() with index as a parameter, and store the result as the new index function.
However, you're missing a level here. A normal decorator, without params, is written like this:
#foo
def bar()
pass
and when the module is imported, foo() is run and returns a function that wraps bar. But you're calling your route() function within the decorator call! So actually your function needs to return a decorator function that itself returns a function that wraps the original function... headscratching, to be sure.
Your route method should look more like this:
def route(self, *rargs, **kargs):
args = list(rargs)
if kargs:
print(kargs['methods'])
def decorator(f):
def wrapped(index_args):
f(args[0])
return wrapped
return decorator
Basically... app.route(index, "/", ["GET", "PUT"]) is a function. And this is the function which is going to be called instead of index.
In your code, when you call index(), it calls app.route(index, "/", ["GET", "PUT"]). This starts by printing kargs['methods'], then creates the decorator function:
def decorator(f):
f(args[0])
This decorator will call the decorated function (f) with one argument, args[0], which here is "/". This prints route: /.
The best explanation of decorators I've found is here: How to make a chain of function decorators?
If you dont want the self-firing, you can define your decorator this way:
def route(*rargs, **kargs):
args = list(rargs)
if kargs:
print(kargs['methods'])
def decorator(f):
f(args[0])
return decorator
#app.route("/", methods = ["GET","PUT"])
def index(rt):
print('route: ' + rt)
However, the rt argument of index will never be used, because route always calls index with args[0] which is always \...

Categories

Resources