To Kill A Mocking Object: A Python Story - python

I have been having trouble with Python mock and have been going crazy. I've held off on this question due to fear of down voting for not enough research. I have a cumulative 24 hours over the last week trying to figure out how to get this work and cannot.
I have read numerous examples and have created this one from those. I know mock objects are supposed to be easy to use, but this has taken too long. Now I am out of time.
I am trying to do two simple things here:
1. Override a request.ok status code inside another function
2. Cause an urllib2.HTTPError exception to be thrown
I have distilled these two tasks into the simplest possible example for your convenience:
#ExampleModule.py
import requests
import urllib2
def hello_world():
try:
print "BEGIN TRY"
r = requests.request('GET', "http://127.0.0.1:80")
print r.ok
if r.ok:
print "PATCH 1 FAILED"
else:
print "PATCH 1 SUCCESSFUL"
except urllib2.HTTPError:
print "PATCH 2 SUCCESSFUL"
print "EXCEPTION 2 HIT\n"
else:
print "PATCH 2 FAILED\n"
and
#in TestModule.py
import mock
import ExampleModule
def test_function_try():
with mock.patch('ExampleModule.hello_world') as patched_request:
patched_request.requests.request.ok = False
result = ExampleModule.hello_world()
print result
def test_function_exception():
with mock.patch('ExampleModule.hello_world') as patched_exception:
patched_exception.urllib2.side_effect = HTTPError
result = ExampleModule.hello_world()
print result
test_function_try()
test_function_exception()
A normal call to hello_world() outputs:
BEGIN TRY
True
<Response [200]>
A normal call to test_function_try() outputs:
<MagicMock name='hello_world()' id='70272816'>
#From the "print result" inside test_function_try()
A normal call to test_function_exception() outputs:
<MagicMock name='hello_world()' id='62320016'>
#From the "print result" inside test_function_exception()
Obviously, I am not actually returning anything from hello_world() so it looks like the patched object is the hello_world() function instead of the requests or urllib2 patched modules.
It should be noted that when I try to patch with 'ExampleModule.hello_world.requests' or 'ExampleModule.hello_world.urllib2' I get an error saying they cannot be found in hello_world()
QUESTION SUMMARY
What is wrong with the two functions test_function_try() and test_function_exception()? What needs to be modified so that I can manually assign the value of request.ok inside hello_world() and also manually raise the exception HTTPError so that I can test the code in that block... Bonus points for explaining 'when' exactly the exception gets thrown: as soon as the try: is entered, or when request is called, or some other time?
Something that has been a concern of mine: will my print statements inside the ExampleModule.py reveal whether my patching and mock tests are working or do I HAVE to use assert methods to get the truth? I am not sure whether assert is a necessity when people mention 'use assertions to find out if the actual patched object was called, etc.' or if this is for convenience/convention/practicality.
UPDATE
After changing the patch target to the requests.request() function, as per #chepner's suggestion, I receive the following output:
BEGIN TRY
False
PATCH 1 SUCCESSFUL
PATCH 2 FAILED
BEGIN TRY
Traceback (most recent call last):
File "C:\LOCAL\ECLIPSE PROJECTS\MockingTest\TestModule.py", line 44, in <module>
test_function_exception()
File "C:\LOCAL\ECLIPSE PROJECTS\MockingTest\TestModule.py", line 19, in test_function_exception
ExampleModule.hello_world()
File "C:\LOCAL\ECLIPSE PROJECTS\MockingTest\ExampleModule.py", line 12, in hello_world
r = requests.request('GET', "http://127.0.0.1:8080")
File "C:\Python27\lib\site-packages\mock\mock.py", line 1062, in __call__
return _mock_self._mock_call(*args, **kwargs)
File "C:\Python27\lib\site-packages\mock\mock.py", line 1118, in _mock_call
raise effect
TypeError: __init__() takes exactly 6 arguments (1 given)

You can't mock local variables in a function. The thing you want to mock is requests.request itself, so that when hello_world calls it, a mock object is returned, rather than actually making an HTTP request.
import mock
import urllib2
import ExampleModule
def test_function_try():
with mock.patch('ExampleModule.requests.request') as patched_request:
patched_request.return_value.ok = False
ExampleModule.hello_world()
def test_function_exception():
with mock.patch('ExampleModule.requests.request') as patched_request:
patched_request.side_effect = urllib2.HTTPError
ExampleModule.hello_world()
test_function_try()
test_function_exception()

Related

Not allowing the developer to use the print method

I have developed a python framework that is being used by others. In order to print any data to the output, the developer should use a Log class (Log.print(...)) and should not use the print() method directly. Is there any ways to force this rule throughout the code? For example, by throwing an error when a developer uses the print method directly like this:
Error: print method cannot be called directly. Please use Log.print().
Suppressing print (as discussed here) is not a good idea as the developer might get confused.
Actullay, below two line code are the same:
sys.stdout.write('hello'+'\n')
print('hello')
so, you can redirect sys.stdout to a class which raise a exception at calling print.
import sys
class BlockPrint():
call_print_exception = Exception('Error: print method cannot be called directly. Please use Log.print().')
def write(self, str):
raise self.call_print_exception
bp = BlockPrint()
sys.stdout=bp
print('aaa')
Output:
Traceback (most recent call last):
File "p.py", line 12, in <module>
print('aaa')
File "p.py", line 7, in write
raise self.call_print_exception
Exception: Error: print method cannot be called directly. Please use Log.print().

concurrent.futures.ThreadPoolExecutor doesn't print errors

I am trying to use concurrent.futures.ThreadPoolExecutor module to run a class method in parallel, the simplified version of my code is pretty much the following:
class TestClass:
def __init__(self, secondsToSleepFor):
self.secondsToSleepFor = secondsToSleepFor
def testMethodToExecInParallel(self):
print("ThreadName: " + threading.currentThread().getName())
print(threading.currentThread().getName() + " is sleeping for " + str(self.secondsToSleepFor) + " seconds")
time.sleep(self.secondsToSleepFor)
print(threading.currentThread().getName() + " has finished!!")
with concurrent.futures.ThreadPoolExecutor(max_workers = 2) as executor:
futuresList = []
print("before try")
try:
testClass = TestClass(3)
future = executor.submit(testClass.testMethodToExecInParallel)
futuresList.append(future)
except Exception as exc:
print('Exception generated: %s' % exc)
If I execute this code it seems to behave like it is intended to.
But if I make a mistake like specifying a wrong number of parameters in "testMethodToExecInParallel" like:
def testMethodToExecInParallel(self, secondsToSleepFor):
and then still submitting the function as:
future = executor.submit(testClass.testMethodToExecInParallel)
or trying to concatenate a string object with an integer object (without using str(.) ) inside a print statement in "testMethodToExecInParallel" method:
def testMethodToExecInParallel(self):
print("ThreadName: " + threading.currentThread().getName())
print("self.secondsToSleepFor: " + self.secondsToSleepFor) <-- Should report an Error here
the program doesn't return any error; just prints "before try" and ends execution...
Is trivial to understand that this makes the program nearly undebuggable... Could someone explain me why such behaviour happens?
(for the first case of mistake) concurrent.futures.ThreadPoolExecutor doesn't check for a function with the specified signature to submit and, eventually, throw some sort of "noSuchFunction" exception?
Maybe there is some sort of problem in submitting to ThreadPoolExecutor class methods instead of simple standalone functions and, so, such behaviour could be expected?
Or maybe the error is thrown inside the thread and for some reason I can't read it?
-- EDIT --
Akshay.N suggestion of inserting future.result() after submitting functions to ThreadPoolExecutor makes the program behave as expected: goes nice if the code is correct, prints the error if something in the code is wrong.
I thing users must be warned about this very strange behaviour of ThreadPoolExecutor:
if you only submit functions to ThreadPoolExecutor WITHOUT THEN CALLING future.result():
- if the code is correct, the program goes on and behaves as expected
- if something in the code is wrong seems the program doesn't call the submitted function, whatever it does: it doesn't report the errors in the code
As far as my knowledge goes which is "not so far", you have to call "e.results()" after "executor.submit(testClass.testMethodToExecInParallel)" in order to execute the threadpool .
I have tried what you said and it is giving me error, below is the code
>>> import concurrent.futures as cf
>>> executor = cf.ThreadPoolExecutor(1)
>>> def a(x,y):
... print(x+y)
...
>>> future = executor.submit(a, 2, 35, 45)
>>> future.result()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "C:\Users\username
\AppData\Local\Programs\Python\Python37\lib\concurrent\futures\_base.py", line
425, in result
return self.__get_result()
File "C:\Users\username
\AppData\Local\Programs\Python\Python37\lib\concurrent\futures\_base.py", line
384, in __get_result
raise self._exception
File "C:\Users\username
\AppData\Local\Programs\Python\Python37\lib\concurrent\futures\thread.py", line
57, in run
result = self.fn(*self.args, **self.kwargs)
TypeError: a() takes 2 positional arguments but 3 were given
Let me know if it still doesn't work

newbiee query: mocking in python

I'm trying to learn Python and mocking infrastructure in Python at the same time (Due to requirement at my work place). I should also mention that I'm also not familiar with mocking feature in C++ or any other language.
So far, from what I've understood is that, with mocking, I can exercise the application code that makes OS. networking etc related calls, without actually invoking those operation.
Let's say I've an application, implemented as network.py
#!/usr/bin/env python
import sys
import socket
class NetworkService(object):
def sock_create(self):
try:
s = socket.socket()
s.close()
print "closed socket"
except Exception, err:
print "error creating socket"
sys.exit(1)
Things that I'd like to achieve with my unit test is:
Make sure that both normal and failure paths get tested.
In this case, to achieve, this I'm trying to come up with a sample unit test case that exercises the sock_create method, as below:
#!/usr/bin/env python
import unittest
import mock
from network import NetworkService
class NetworkServiceTest(unittest.TestCase):
#mock.patch('network.socket')
def test_01_sock_create(self, mock_sock):
reference = NetworkService()
mock_sock.return_value = False
# NetworkService::sock_create::s.close() should NOT get called
reference.sock_create()
self.assertFalse(mock_sock.close.called, "Failed to not call close")
mock_sock.socket.return_value = True
# NetworkService::sock_create::s.close() should get called
reference.sock_create()
# how to test this ???
#mock_sock.close.assert_called_with("")
if __name__ == '__main__':
unittest.main()
As you can see above, the last 'assert' statement is currently commented out; I'm not sure, how to check this? The following gives me error:
#!/usr/bin/env python
import unittest
import mock
from network import NetworkService
class NetworkServiceTest(unittest.TestCase):
#mock.patch('network.socket')
def test_01_sock_create(self, mock_sock):
reference = NetworkService()
mock_sock.return_value = False
reference.sock_create()
self.assertFalse(mock_sock.close.called, "Failed to not call close")
mock_sock.socket.return_value = True
reference.sock_create()
self.assertTrue(mock_sock.close.called, "Should have called s.close")
if __name__ == '__main__':
unittest.main()
and the error:
$ python tester.py
F
======================================================================
FAIL: test_01_sock_create (__main__.NetworkServiceTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/usr/lib/python2.7/site-packages/mock/mock.py", line 1305, in patched
return func(*args, **keywargs)
File "tester.py", line 17, in test_01_sock_create
self.assertTrue(mock_sock.close.called, "Should have called s.close")
AssertionError: Should have called s.close
----------------------------------------------------------------------
Ran 1 test in 0.002s
FAILED (failures=1)
closed socket
error creating socket
NOTE that I'm using mocking in Python 2.7 (mock need to be installed as a separate module)
In network.py you are printing out a string. If you instead would print out the actual error you would see the reason why it's failing. What you would see in this case is that it's failing because of an AttributeError. AttributeError("'bool' object has no attribute 'close'",)
The reason this is happening is because you're giving the mock object the return value of True or False. Since a bool doesn't have any open or close method it'll throw that error.
A few other tips. You're using Exception which will catch all exceptions. Instead, only catch exceptions that you know will happen. Find out what exceptions socket might throw.
That way you would have discovered this earlier.

Can't catch mocked exception because it doesn't inherit BaseException

I'm working on a project that involves connecting to a remote server, waiting for a response, and then performing actions based on that response. We catch a couple of different exceptions, and behave differently depending on which exception is caught. For example:
def myMethod(address, timeout=20):
try:
response = requests.head(address, timeout=timeout)
except requests.exceptions.Timeout:
# do something special
except requests.exceptions.ConnectionError:
# do something special
except requests.exceptions.HTTPError:
# do something special
else:
if response.status_code != requests.codes.ok:
# do something special
return successfulConnection.SUCCESS
To test this, we've written a test like the following
class TestMyMethod(unittest.TestCase):
def test_good_connection(self):
config = {
'head.return_value': type('MockResponse', (), {'status_code': requests.codes.ok}),
'codes.ok': requests.codes.ok
}
with mock.patch('path.to.my.package.requests', **config):
self.assertEqual(
mypackage.myMethod('some_address',
mypackage.successfulConnection.SUCCESS
)
def test_bad_connection(self):
config = {
'head.side_effect': requests.exceptions.ConnectionError,
'requests.exceptions.ConnectionError': requests.exceptions.ConnectionError
}
with mock.patch('path.to.my.package.requests', **config):
self.assertEqual(
mypackage.myMethod('some_address',
mypackage.successfulConnection.FAILURE
)
If I run the function directly, everything happens as expected. I even tested by adding raise requests.exceptions.ConnectionError to the try clause of the function. But when I run my unit tests, I get
ERROR: test_bad_connection (test.test_file.TestMyMethod)
----------------------------------------------------------------
Traceback (most recent call last):
File "path/to/sourcefile", line ###, in myMethod
respone = requests.head(address, timeout=timeout)
File "path/to/unittest/mock", line 846, in __call__
return _mock_self.mock_call(*args, **kwargs)
File "path/to/unittest/mock", line 901, in _mock_call
raise effect
my.package.requests.exceptions.ConnectionError
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "Path/to/my/test", line ##, in test_bad_connection
mypackage.myMethod('some_address',
File "Path/to/package", line ##, in myMethod
except requests.exceptions.ConnectionError:
TypeError: catching classes that do not inherit from BaseException is not allowed
I tried to change the exception I was patching in to BaseException and I got a more or less identical error.
I've read https://stackoverflow.com/a/18163759/3076272 already, so I think it must be a bad __del__ hook somewhere, but I'm not sure where to look for it or what I can even do in the mean time. I'm also relatively new to unittest.mock.patch() so it's very possible that I'm doing something wrong there as well.
This is a Fusion360 add-in so it is using Fusion 360's packaged version of Python 3.3 - as far as I know it's a vanilla version (i.e. they don't roll their own) but I'm not positive of that.
I could reproduce the error with a minimal example:
foo.py:
class MyError(Exception):
pass
class A:
def inner(self):
err = MyError("FOO")
print(type(err))
raise err
def outer(self):
try:
self.inner()
except MyError as err:
print ("catched ", err)
return "OK"
Test without mocking :
class FooTest(unittest.TestCase):
def test_inner(self):
a = foo.A()
self.assertRaises(foo.MyError, a.inner)
def test_outer(self):
a = foo.A()
self.assertEquals("OK", a.outer())
Ok, all is fine, both test pass
The problem comes with the mocks. As soon as the class MyError is mocked, the expect clause cannot catch anything and I get same error as the example from the question :
class FooTest(unittest.TestCase):
def test_inner(self):
a = foo.A()
self.assertRaises(foo.MyError, a.inner)
def test_outer(self):
with unittest.mock.patch('foo.MyError'):
a = exc2.A()
self.assertEquals("OK", a.outer())
Immediately gives :
ERROR: test_outer (__main__.FooTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "...\foo.py", line 11, in outer
self.inner()
File "...\foo.py", line 8, in inner
raise err
TypeError: exceptions must derive from BaseException
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "<pyshell#78>", line 8, in test_outer
File "...\foo.py", line 12, in outer
except MyError as err:
TypeError: catching classes that do not inherit from BaseException is not allowed
Here I get a first TypeErrorthat you did not have, because I am raising a mock while you forced a true exception with 'requests.exceptions.ConnectionError': requests.exceptions.ConnectionError in config. But the problem remains that the except clause tries to catch a mock.
TL/DR: as you mock the full requests package, the except requests.exceptions.ConnectionError clause tries to catch a mock. As the mock is not really a BaseException, it causes the error.
The only solution I can imagine is not to mock the full requests but only the parts that are not exceptions. I must admit I could not find how to say to mock mock everything except this but in your example, you only need to patch requests.head. So I think that this should work :
def test_bad_connection(self):
with mock.patch('path.to.my.package.requests.head',
side_effect=requests.exceptions.ConnectionError):
self.assertEqual(
mypackage.myMethod('some_address',
mypackage.successfulConnection.FAILURE
)
That is : only patch the head method with the exception as side effect.
I just ran into the same issue while trying to mock sqlite3 (and found this post while looking for solutions).
What Serge said is correct:
TL/DR: as you mock the full requests package, the except requests.exceptions.ConnectionError clause tries to catch a mock. As the mock is not really a BaseException, it causes the error.
The only solution I can imagine is not to mock the full requests but only the parts that are not exceptions. I must admit I could not find how to say to mock mock everything except this
My solution was to mock the entire module, then set the mock attribute for the exception to be equal to the exception in the real class, effectively "un-mocking" the exception. For example, in my case:
#mock.patch(MyClass.sqlite3)
def test_connect_fail(self, mock_sqlite3):
mock_sqlite3.connect.side_effect = sqlite3.OperationalError()
mock_sqlite3.OperationalError = sqlite3.OperationalError
self.assertRaises(sqlite3.OperationalError, MyClass, self.db_filename)
For requests, you could assign exceptions individually like this:
mock_requests.exceptions.ConnectionError = requests.exceptions.ConnectionError
or do it for all of the requests exceptions like this:
mock_requests.exceptions = requests.exceptions
I don't know if this is the "right" way to do it, but so far it seems to work for me without any issue.
For those of us who need to mock an exception and can't do that by simply patching head, here is an easy solution that replaces the target exception with an empty one:
Say we have a generic unit to test with an exception we have to have mocked:
# app/foo_file.py
def test_me():
try:
foo()
return "No foo error happened"
except CustomError: # <-- Mock me!
return "The foo error was caught"
We want to mock CustomError but because it is an exception we run into trouble if we try to patch it like everything else. Normally, a call to patch replaces the target with a MagicMock but that won't work here. Mocks are nifty, but they do not behave like exceptions do. Rather than patching with a mock, let's give it a stub exception instead. We'll do that in our test file.
# app/test_foo_file.py
from mock import patch
# A do-nothing exception we are going to replace CustomError with
class StubException(Exception):
pass
# Now apply it to our test
#patch('app.foo_file.foo')
#patch('app.foo_file.CustomError', new_callable=lambda: StubException)
def test_foo(stub_exception, mock_foo):
mock_foo.side_effect = stub_exception("Stub") # Raise our stub to be caught by CustomError
assert test_me() == "The error was caught"
# Success!
So what's with the lambda? The new_callable param calls whatever we give it and replaces the target with the return of that call. If we pass our StubException class straight, it will call the class's constructor and patch our target object with an exception instance rather than a class which isn't what we want. By wrapping it with lambda, it returns our class as we intend.
Once our patching is done, the stub_exception object (which is literally our StubException class) can be raised and caught as if it were the CustomError. Neat!
I faced a similar issue while trying to mock the sh package. While sh is very useful, the fact that all methods and exceptions are defined dynamically make it more difficult to mock them. So following the recommendation of the documentation:
import unittest
from unittest.mock import Mock, patch
class MockSh(Mock):
# error codes are defined dynamically in sh
class ErrorReturnCode_32(BaseException):
pass
# could be any sh command
def mount(self, *args):
raise self.ErrorReturnCode_32
class MyTestCase(unittest.TestCase):
mock_sh = MockSh()
#patch('core.mount.sh', new=mock_sh)
def test_mount(self):
...
I just ran into the same problem when mocking struct.
I get the error:
TypeError: catching classes that do not inherit from BaseException is not allowed
When trying to catch a struct.error raised from struct.unpack.
I found that the simplest way to get around this in my tests was to simply set the value of the error attribute in my mock to be Exception. For example
The method I want to test has this basic pattern:
def some_meth(self):
try:
struct.unpack(fmt, data)
except struct.error:
return False
return True
The test has this basic pattern.
#mock.patch('my_module.struct')
def test_some_meth(self, struct_mock):
'''Explain how some_func should work.'''
struct_mock.error = Exception
self.my_object.some_meth()
struct_mock.unpack.assert_called()
struct_mock.unpack.side_effect = struct_mock.error
self.assertFalse(self.my_object.some_meth()
This is similar to the approach taken by #BillB, but it is certainly simpler as I don't need to add imports to my tests and still get the same behavior. To me it would seem this is the logical conclusion to the general thread of reasoning in the answers here.
Use patch.object to partially mock a class.
My use case:
import unittest
from unittest import mock
import requests
def test_my_function(self):
response = mock.MagicMock()
response.raise_for_status.side_effect = requests.HTTPError
with mock.patch.object(requests, 'get', return_value=response):
my_function()

Python – How to mock a single function

From the Mock docs, I wasn't able to understand how to implement the following type of pattern successfully. fetch_url does not exist inside of a class.
My function in the auth.py file:
def fetch_url(url, method=urlfetch.GET, data=''):
"""Send a HTTP request"""
result = urlfetch.fetch(url=url, method=method, payload=data,
headers={'Access-Control-Allow-Origin': '*'})
return result.content
My test:
import unittest
from mock import Mock
class TestUrlFetch(unittest.TestCase):
def test_fetch_url(self):
from console.auth import fetch_url
# Create a mock object based on the fetch_url function
mock = Mock(spec=fetch_url)
# Mock the fetch_url function
content = mock.fetch_url('https://google.com')
# Test that content is not empty
self.assertIsNotNone(content)
If what I'm doing is completely in the wrong direction, please shed some light on the correct solution.
The test is not working, and is producing the following error:
======================================================================
ERROR: test_fetch_url (console.tests.test_auth.TestUrlFetch)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/Users/bengrunfeld/Desktop/Work/code/wf-ghconsole/console/tests/test_auth.py", line 34, in test_fetch_url
content = mock.fetch_url('https://google.com')
File "/Users/bengrunfeld/.virtualenvs/env2/lib/python2.7/site-packages/mock.py", line 658, in __getattr__
raise AttributeError("Mock object has no attribute %r" % name)
AttributeError: Mock object has no attribute 'fetch_url'
-------------------- >> begin captured logging << --------------------
root: DEBUG: Using threading.local
--------------------- >> end captured logging << ---------------------
----------------------------------------------------------------------
Ran 1 test in 0.277s
FAILED (errors=1)
First of all, as univerio's comment suggests you should call you mock like this:
mock('https://google.com')
Your test should pass after that fix, but probably that mock doesn't do what you really want. I've encountered a few problems with spec and autospec.
Mocks created with Mock(spec=) don't check number of arguments they are called with. I've just looked through the docs and they don't state that, but for some reason I expected it to work. Autospecced mocks do check the arguments.
By default both spec and autospec function mocks return mock objects when you call them. This may be not what you want when you mock a function that does not return anything. In this case you can set the return_value manually:
def foo():
pass
mock_foo = Mock(spec=foo, return_value=None)
mock_foo()

Categories

Resources