How to patch a function that a Flask view calls - python

My webapp wants to send a message to AWS SQS with boto and I'd want to mock out sending the actual message and just checking that calling send_message is called. However I do not understand how to use python mock to patch a function that a function being tested calls.
How could I achieve mocking out boto con.send_message as in the pseudo-like code below?
views.py:
#app.route('/test')
def send_msg():
con = boto.sqs.connect_to_region("eu-west-1",aws_access_key_id="asd",aws_secret_access_key="asd")
que = con.get_queue('my_queue')
msg = json.dumps({'data':'asd'})
r=con.send_message(que, msg)
tests.py
class MyTestCase(unittest.TestCase):
def test_test(self):
with patch('views.con.send_message') as sqs_send:
self.test_client.get('/test')
assert(sqs_send.called)

To do this kind of test you need patch connect_to_region(). When this method is patched return a MagicMock() object that you can use to test all your function behavior.
Your test case can be something like this one:
class MyTestCase(unittest.TestCase):
#patch("boto.sqs.connect_to_region", autospec=True)
def test_test(self, mock_connect_to_region):
#grab the mocked connection returned by patched connect_to_region
mock_con = mock_connect_to_region.return_value
#call client
self.test_client.get('/test')
#test connect_to_region call
mock_connect_to_region.assert_called_with("eu-west-1",aws_access_key_id="asd",aws_secret_access_key="asd")
#test get_queue()
mock_con.get_queue.assert_called_with('my_queue')
#finaly test send_message
mock_con.send_message.assert_called_with(mock_con.get_queue.return_value, json.dumps({'data':'asd'}))
Just some notes:
I wrote it in a white box style and check all calls of your view: you can do it more loose and omit some checks; use self.assertTrue(mock_con.send_message.called) if you want just check the call or use mock.ANY as argument if you are not interested in some argument content.
autospec=True is not mandatory but very useful: take a look at autospeccing.
I apologize if code contains some error... I cannot test it now but I hope the idea is clear enough.

Related

Unit testing lambdas which call dynamodb in python

I am having lambdas which use boto3.client() to connect to a dynamoDB.
I tried to test it like this
#mock.patch("boto3.client")
def test(self, mock_client, test):
handler(event, context)
print(mock_client.call_count) # 1
print(mock_client.put_item.call_count) # 0
However, the mock_client.call_count is 1, but not the put_item_call_count.
My handler looks like this:
def handler(event, context):
dynamodb = boto3.client('dynamodb')
response = dynamodb.put_item(// same attributed)
Any suggestion, how to test if the correct item gots putted in the database without using moto?
I believe you're very close, there's just one tiny problem.
When your mocked boto3.client is called, it returns another mock and you want to evaluate that mocks call_count. By accessing the return_value of the original mock, you get access to the created magic mock.
#mock.patch("boto3.client")
def test(self, mock_client, test):
handler(event, context)
print(mock_client.call_count)
# .return_value refers to the magic mock that's
# created when boto3.client is called
print(mock_client.return_value.put_item.call_count)
What you're currently evaluating is the call count of boto3.client.put_item and not boto3.client("dynamodb").put_item().

Python unittest test passed in class method is called with

I have a method that takes two inputs a emailer class and a dict of data.
def send_email(data, email_client):
**** various data checks and formatting *****
response_code = email_client.create_email(recipient=receipient
sender=sender
etc...)
I am trying to write a unit test that will assert email_client.create_email was called with the correct values based on the input data.
In my test file I have
from emailer.email import send_email
class TestEmails(unittest.TestCase):
def test_send_email(self):
email.send_email(get_transactional_email_data, MagicMock())
I normally test what a method is called with by something similar to:
mock.assert_called_with(recipient=receipient
sender=sender
etc..)
However, since this time I'm testing what a passed in class is being called with (and a MagicMock) I don't know how it should be done.
I don't think you need MagicMock. Just create the mocks upfront
from emailer.email import send_email
class TestEmails(unittest.TestCase):
def test_send_email(self):
myclient = Mock()
mycreate = Mock()
myclient.create_email = mycreate
email.send_email(get_transactional_email_data, myclient)
self.assertTrue(
mycreate.called_with(sender='...')
)

How do I mock a method that uses requests.get in my class?

I'm attempting to create a few unit tests for my class. I want to mock these, so that I don't burn through my API quota running some of these tests. I have multiple test cases that will call the fetch method, and depending on the passed URL I'll get different results back.
My example class looks like this:
import requests
class ExampleAPI(object):
def fetch(self, url, params=None, key=None, token=None, **kwargs):
return requests.get(url).json() # Returns a JSON string
The tutorial I'm looking at shows that I can do something like this:
import unittest
from mock import patch
def fake_fetch_test_one(url):
...
class TestExampleAPI(unittest.TestCase):
#patch('mymodule.ExampleAPI.fetch', fake_fetch_test_one)
def test_fetch(self):
e = ExampleAPI()
self.assertEqual(e.fetch('http://my.api.url.example.com'), """{'result': 'True'}""")
When I do this, though, I get an error that says:
TypeError: fake_fetch_test_one() takes exactly 1 argument (3 given)
What is the proper way to mock a requests.get call that is in a method in my class? I'll need the ability to change the mock'd response per test, because different URLs can provide different response types.
Your fake fetch needs to accept the same arguments as the original:
def fake_fetch(self, url, params=None, key=None, token=None, **kwargs):
Note that it's better to mock just the external interface, which means letting fetch call requests.get (or at least, what it thinks is requests.get):
#patch('mymodule.requests.get')
def test_fetch(self, fake_get):
# It would probably be better to just construct
# a valid fake response object whose `json` method
# would return the right thing, but this is a easier
# for demonstration purposes. I'm assuming nothing else
# is done with the response.
expected = {"result": "True"}
fake_get.return_value.json.return_value = expected
e = ExampleAPI()
self.assertEqual(e.fetch('http://my.api.url.example.com'), expected)
from you test method you can monkeypatch your requests module
import unittest
class Mock:
pass
ExampleAPI.requests = Mock()
def fake_get_test_one(url):
/*returns fake get json */
ExampleAPI.requests.get= Mock()
ExampleAPI.requests.json = fake_get_test_one
class TestExampleAPI(unittest.TestCase):
def test_fetch(self):
e = ExampleAPI()
self.assertEqual(e.fetch('http://my.api.url.example.com'), """{'result': 'True'}""")
you can setup the patch in each setup() and corresponding teardown() methods of your test class if needed

Python internal entity mocking

I'd like to test a method, whether it calls a specific method of a temporary internal object or not. (ConfigParser.read)
So the object is created inside, and it's not accessible from the outside after the method exits.
Using python 2.7
In foobar.py
import ConfigParser
class FooBar:
def method(self, filename):
config=ConfigParser.ConfigParser()
config.read(filename)
do_some_stuff()
I'd like to test whether config.read was called.
As I understand, the patch decorator was made for this, but unfortunately the MagicMock object the testcase receives is not the same that is created inside, and I can't get near the object that lives inside the method.
I tried like this:
class TestFooBar(TestCase):
def setUp(self):
self.myfoobar = FooBar()
#mock.patch('foobar.ConfigParser')
def test_read(self,mock_foobar):
self.myfoobar.method("configuration.ini")
assert mock_foobar.called # THIS IS OKAY
assert mock_foobar.read.called # THIS FAILS
mock_foobar.read.assert_called_with("configuration.ini") # FAILS TOO
The problem is:
- mock_foobar is created before the self.myfoobar.method creates the ConfigReader inside.
- when debugging mock_foobar has internal data about the previous calls, but no "read" property (the inner MagicMock for mocking the read method)
Of course one way out is refactoring and giving the .read() or the init() a ConfigReader object, but it's not always possible to change the code, and I'd like to grasp the internal objects of the method without touching the module under test.
You're so close! The issue is that you are mocking the class, but then your test checks that read() is called on that mock class - but you actually expect read() to be called on the instance that is returned when you call the class. The following works - I find the second test more readable than the first, but they both work:
import ConfigParser
from unittest import TestCase
from mock import create_autospec, patch, Mock
class FooBar(object):
def method(self, filename):
config=ConfigParser.ConfigParser()
config.read(filename)
class TestFooBar(TestCase):
def setUp(self):
self.myfoobar = FooBar()
#patch('ConfigParser.ConfigParser')
def test_method(self, config_parser_class_mock):
config_parser_mock = config_parser_class_mock.return_value
self.myfoobar.method("configuration.ini")
config_parser_class_mock.assert_called_once_with()
config_parser_mock.read.assert_called_once_with("configuration.ini")
def test_method_better(self):
config_parser_mock = create_autospec(ConfigParser.ConfigParser, instance=True)
config_parser_class_mock = Mock(return_value=config_parser_mock)
with patch('ConfigParser.ConfigParser', config_parser_class_mock):
self.myfoobar.method("configuration.ini")
config_parser_class_mock.assert_called_once_with()
config_parser_mock.read.assert_called_once_with("configuration.ini")

Python: no such test method: How can I execute a test method by calling it explicitly from another method?

Here is my LoginResourceHelper Test class
from flask.ext.testing import TestCase
class LoginResourceHelper(TestCase):
content_type = 'application/x-www-form-urlencoded'
def test_create_and_login_user(self, email, password):
user = UserHelper.add_user(email, password)
self.assertIsNotNone(user)
response = self.client.post('/', content_type=self.content_type,
data=UserResourceHelper.get_user_json(
email, password))
self.assert200(response)
# HTTP 200 OK means the client is authenticated and cookie
# USER_TOKEN has been set
return user
def create_and_login_user(email, password='password'):
"""
Helper method, also to abstract the way create and login works.
Benefit? The guts can be changed in future without breaking the clients
that use this method
"""
return LoginResourceHelper().test_create_and_login_user(email, password)
When I call create_and_login_user('test_get_user'), I see error as following
line 29, in create_and_login_user
return LoginResourceHelper().test_create_and_login_user(email, password)
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/unittest/case.py", line 191, in __init__
(self.__class__, methodName))
ValueError: no such test method in <class 'core.expense.tests.harness.LoginResourceHelper.LoginResourceHelper'>: runTest
Python's unittest module (which Flask is using behind the scenes) organizes the code in a special way.
In order to run a specific method from a class that is derived of TestCase you need to do the following:
LoginResourceHelper('test_create_and_login_user').test_create_and_login_user(email, password)
What untitest does behind the scenes
In order to understand why you must do this, you need to understand how the default TestCase object works.
Normally, when inherited, TestCase is expecting to have a runTest method:
class ExampleTestCase(TestCase):
def runTest(self):
# Do assertions here
However if you needed to have multiple TestCases you would need to do this for every single one.
Since this is a tedious thing to do, they decided to the following:
class ExampleTestcase(TestCase):
def test_foo(self):
# Do assertions here
def test_bar(self):
# Do other assertions here
This is called a Test Fixture. But since we did not declare a runTest(), you now must specify what method you want the TestCase to run - which is what you want to do.
>>ExampleTestCase('test_foo').test_foo()
>>ExampleTestCase('test_bar').test_bar()
Normally, the unittest module will do all of this on the back end, along with some other things:
Adding TestCases to a Test Suite (which is normally done by using a TestLoader)
Calling the correct TestRunner (which will run all of the tests and report the results)
But since you are circumventing the normal unittest execution, you have to do the work that unitest regularly does.
For a really indepth understanding, I highly recommend that you read the docs unittest.

Categories