Python – How to mock a single function - python

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()

Related

To Kill A Mocking Object: A Python Story

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()

Twisted deferToThread, not working with Mock.patch()

I have 2 functions both wrapped with #defer.inlineCallbacks decorator.
For tests, I'm mocking various things, including the save_to_db() function.
logic.py
#defer.inlineCallbacks
def save_to_db(obj): # mocked for test.
raise Exception('Oh Noe!')
#defer.inlineCallbacks
def create_contact():
xero = yield get_xero_client()
data = {
# <contact info>
}
# Create a new contact
response = yield deferToThread(xero.contacts.put, data)
obj = {
# some data extracted from response
}
yield save_to_db(obj)
tests.py
import mock
from twisted.internet import defer
from twisted.trial import unittest
from .logic import create_contact
class TestContactCreation(unittest.TestCase):
#mock.patch('logic.save_to_db')
#mock.patch('logic.get_xero_client')
#defer.inlineCallbacks
def test_get_xero_client_is_called(self, mocked_xero_client, mocked_save_method):
yield create_contact()
mocked_get_xero_client.assert_called()
However when I run:
$ trial tests.TestContactCreation
save_to_db() is actually called and as expected its raises an Exception.
Traceback (most recent call last):
File "<file_path>/logic.py", line 93, in save_to_db
raise Exception('Oh Noe!')
exceptions.Exception: Oh Noe!
And I'm not sure why! I tried to debug using pdb.
import pdb; pdb.set_trace()
It looks like save_to_db() is mocked correctly before we use deferToThread()
(Pdb) save_to_db
<MagicMock name='save_to_db' id='4404276240'>
However after the line where I've used deferToThread()
(Pdb) save_to_db
<function save_to_db at 0x111c6f488>
save_to_db() is no longer mocked! Only way I can get around this is if I also mock deferToThread()
Is there a better option? Any tips will be appreciated. Many Thanks.
I encountered the same issue; the #mock.patch(...) decorator doesn't work (i.e., it doesn't actually mock the desired thing) when used in conjunction with the #inlineCallbacks decorator.
What did work for me was to mock via context manager:
#defer.inlineCallbacks
def test_get_xero_client_is_called(self):
with mock.patch('logic.save_to_db') as mocked_save_method, \
mock.patch('logic.get_xero_client') as mocked_xero_client:
yield create_contact()
mocked_get_xero_client.assert_called()
Does that work for you?

Conflict between mock.patch and unittest.skip

(Python 3.4.0)
I got this strange error, which took me a while to debug:
user.py
class User:
def __init__(self, name):
self.name = name
def new_user(name):
user = User(name)
test.py
import unittest
from unittest.mock import Mock, patch
from user import new_user
#patch('user.User')
class TestUser(unittest.TestCase):
#unittest.skip
def test_new_user(self, mockUser):
new_user('Frank')
mockUser.assert_called_once_with('Frank')
unittest.main()
Running it will crash:
» python test.py
E
======================================================================
ERROR: test_new_user (__main__.TestUser)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/usr/lib/python3.4/unittest/mock.py", line 1125, in patched
return func(*args, **keywargs)
TypeError: decorator() takes 1 positional argument but 2 were given
----------------------------------------------------------------------
Ran 1 test in 0.001s
FAILED (errors=1)
Removing the skip will let it run normally. It seems patch and skip do not stack well. Is this correct, or am I doing something stupid?
unittest.skip requires a string argument of its own, the reason for skipping the test.
#unittest.skip("Not yet ready to test")
def test_new_user(self, mockUser):
new_user('Frank')
mockUser.assert_called_once_with('Frank')
The interaction you are seeing comes from the skip decorator consuming the method itself as the reason argument (def skip(reason):), which results in test_new_user being bound to a one-argument function defined inside the decorator, not the two-argument function you define in the test case.
Note that if you left your call to skip in place and commented out the patch instead, your test would still pass, despite test_new_user seemingly not receiving its mockUser argument.
unittest.skip itself is technically not a decorator; it is a function which returns a decorator, which is then applied to test_new_user. Using regular function-call syntax, your code does
def test_new_user(self, mockUser):
...
test_new_user = unittest.skip(test_new_user)
when what you need is
test_new_user = unittest.skip("my reason")(test_new_user)
Your test_new_user is being bound to the decorator itself, not the decorated method.

Python: assertRaises( ) not catching ldap.SERVER_DOWN error when raised

Thanks for your help in advance.
I've got the following class method that I'm trying to test:
def _get_ldap_connection(self):
"""
Instantiate and return simpleldap.Connection object.
Raises:
ldap.SERVER_DOWN: When ldap_url is invalid or server is
not reachable.
"""
try:
ldap_connection = simpleldap.Connection(
self.ldap_url, encryption='ssl', require_cert=False,
debug=False, dn=self.ldap_login_dn,
password=self.ldap_login_password)
except ldap.SERVER_DOWN:
raise ldap.SERVER_DOWN(
"The LDAP server specified, {}, did not respond to the "
"connection attempt.".format(self.ldap_url))
And here's the unittest:
def test__get_ldap_connection(self):
"""
VERY IMPORTANT: This test refers to your actual config.json file.
If it is correctly populated, you can expect this test to fail.
"""
# Instantiate Class
test_extractor = SakaiLdapExtractor('config_files/config.json')
# Monkey with ldap server url to ensure error.
test_extractor.ldap_url = "invalid_ldap_url"
self.assertRaises(
ldap.SERVER_DOWN, test_extractor._get_ldap_connection())
So far, so good. But when I execute the unit tests (via nose) test_extractor._get_ldap_connection() is called from the assertRaises statement, but the exception is not caught and the test fails.
Here is the output:
vagrant#precise64:/vagrant/sakai-directory-integration$ nosetests
...E..
======================================================================
ERROR: VERY IMPORTANT: This test refers to your actual config.json file.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/vagrant/sakai-directory-integration/test_sakaiLdapExtractor.py", line 77, in test__get_ldap_connection
ldap.SERVER_DOWN, test_extractor._get_ldap_connection())
File "/vagrant/sakai-directory-integration/sakai_ldap_integration.py", line 197, in _get_ldap_connection
"connection attempt.".format(self.ldap_url))
SERVER_DOWN: The LDAP server specified, invalid_ldap_url, did not respond to the connection attempt.
----------------------------------------------------------------------
Ran 6 tests in 0.159s
Help me!
Don't call, just pass function (method) itself; drop ():
self.assertRaises(
ldap.SERVER_DOWN, test_extractor._get_ldap_connection)
Alternatively, you can use with self.assertRaises(..) form if you are using recent version of python (Python 2.7+ / Python 3.1+):
with self.assertRaises(ldap.SERVER_DOWN):
test_extractor._get_ldap_connection()
You are not using assertRaises correctly.
You can use it as a context manager:
with self.assertRaises(ldap.SERVER_DOWN):
test_extractor._get_ldap_connection()
or the usual way (self.assertRaises(exception, function, args):
self.assertRaises(ldap.SERVER_DOWN, test_extractor._get_ldap_connection)
Also see:
How to properly use unit-testing's assertRaises() with NoneType objects?
Testing in Python - how to use assertRaises in testing using unittest?
documentation

Nose: generator for TestCase-based classes

I want to create a generator for variations of a TestCase-derived class.
What I tried is this:
import unittest
def create_class(param):
class Test(unittest.TestCase):
def setUp(self):
pass
def test_fail(self):
assert False
return Test
def test_basic():
for i in range(5):
yield create_class(i)
What I get is this:
======================================================================
ERROR: test_1.test_basic
----------------------------------------------------------------------
Traceback (most recent call last):
File "/usr/lib/python3.3/site-packages/nose/case.py", line 268, in setUp
try_run(self.test, names)
File "/usr/lib/python3.3/site-packages/nose/util.py", line 478, in try_run
return func()
TypeError: setUp() missing 1 required positional argument: 'self'
Yielding instances instead of classes (yield create_class(i)()) leaves me with this error:
======================================================================
ERROR: test_1.test_basic
----------------------------------------------------------------------
Traceback (most recent call last):
File "/usr/lib/python3.3/site-packages/nose/case.py", line 198, in runTest
self.test(*self.arg)
File "/usr/lib/python3.3/unittest/case.py", line 492, in __call__
return self.run(*args, **kwds)
File "/usr/lib/python3.3/unittest/case.py", line 423, in run
testMethod = getattr(self, self._testMethodName)
AttributeError: 'Test' object has no attribute 'runTest'
Any ideas?
When instantiating a TestCase you should pass the method name of the test:
yield create_class(i)('test_fail')
Otherwise the name defaults to runTest(and thus the last error you got).
Also note that there is a strange interaction between test generators and TestCase. With the following code:
import unittest
def create_class(param):
class Test(unittest.TestCase):
def setUp(self):
pass
def test_fail(self):
print('executed')
assert False
print('after assert')
return Test
def test_basic():
for i in range(5):
yield create_class(i)('test_fail')
I obtain this output:
$ nosetests -s
executed
.executed
.executed
.executed
.executed
.
----------------------------------------------------------------------
Ran 5 tests in 0.004s
OK
As you can see the test does not fail, even though the assert works. This is probably due to the fact that TestCase handles the AssertionError but nose does not expect this to be handled and thus it cannot see that the test failed.
This can be seen from the documentation of TestCase.run:
Run the test, collecting the result into the test result object passed as result. If result is omitted or None, a temporary result
object is created (by calling the defaultTestResult() method) and
used. The result object is not returned to run()‘s caller.
The same effect may be had by simply calling the TestCase instance.
So, nose doesn't see that the objected yielded by the generator is a TestCase which should be handled in a special manner, it simply expects a callable. The TestCase is run, but the result is put into a temporary object that is lost, and this eats all test failures that happen inside the tests. Hence yielding TestCasees simply doesn't work.
I have run the codes you provides. I received no error. The version I use is python2.7. System is ubuntu12.10. Maybe you need to check with python2.7.

Categories

Resources