I have a view that perform a third party API call:
from .atoca_api_calls import atoca_api_call
def atoca_api_call_view(request):
# some code
data = atoca_api_call(**params) # the actual API call happens in this func
# some code
My intention is to mock just the atoca_api_call() func and not the whole view.
class AtocaTestCase(TestCase):
def setUp(self):
# some code
#patch('crm.atoca_api_calls.atoca_api_call')
def test_atoca_call(self, mock_atoca_api_call):
mock_atoca_api_call.return_value = MagicMock(
status_code=200,
response=some_json # just returns some json
)
url = reverse('crm:atoca-api-call') # url related to `atoca_api_call_view`
response = self.client.post(url, some_others_params)
# various asserts
The test works fine but atoca_api_call() is not mocked.
I'm aware of where to patch:
#patch('crm.views.atoca_api_call', autospec=True)
Raises a ValueError:
ValueError: Failed to insert expression "<MagicMock name='mock.__getitem__().__getitem__().__getitem__()().resolve_expression()' id='140048216397424'>" on crm.Company.atoca_id. F() expressions can only be used to update, not to insert.
It's probably a simple issue I don't catch for inexperience, any help is really appreciated.
As i see the atoca_api_call() it is mocked. The issue is the return value.
mock_atoca_api_call.return_value = MagicMock(
status_code=200,
response=some_json # just returns some json
)
This should ve a proper response i assume a JsonResponse or Response(data) not sure what are you returning. Try with:
mock_atoca_api_call.return_value = JsonResponse(
some_json # just returns some json
)
Related
I'm writing some tests and I'd like to convert flask.Response objects to corresponding requests.Response objects. So I have 2 Flask apps (e.g. A and B), and A makes internal calls to B (via requests.post(url, json=payload)). My goal is to properly mock those calls without even launching any servers, and the current solution looks like the following:
from unittest import mock
...
def mock_B_request(url, json):
response = app_B.test_client().post(url, json=json) # flask.Response
# Some hacking should be done here,
# since flask.Respone doesn't have `.ok`, `.json()`, etc.,
# so it will break the code inside app_A
return response
...
# Inside the actual test method
with mock.patch('requests.post', side_effect=mock_B_request):
response = app_A.test_client().post(url, json=payload)
result = response.get_json()
...
Has someone already encountered with such a problem? What is the easiest solution here?
Eventually, I have found a very easy working solution:
def mock_B_request(url, **kwargs):
response = app_B.test_client().post(url, json=kwargs['json'])
# Hack a bit in order to make flask.Response
# support the same API as requests.Response
response_mock = mock.MagicMock()
response.status_code = response.status_code
response_mock.ok = response.status_code == 200
response_mock.json = lambda: response.get_json()
return response_mock
I hope it will be helpful for somebody.
I already know that one can implement a class that inherits from SimpleTestCase, and one can test redirection by:
SimpleTestCase.assertRedirects(response, expected_url, status_code=302, target_status_code=200, host=None, msg_prefix='', fetch_redirect_response=True)
However, I am wondering what is the way I can check for redirection using pytest:
#pytest.mark.django_db
def test_redirection_to_home_when_group_does_not_exist(create_social_user):
"""Some docstring defining what the test is checking."""
c = Client()
c.login(username='TEST_USERNAME', password='TEST_PASSWORD')
response = c.get(reverse('pledges:home_group',
kwargs={'group_id': 100}),
follow=True)
SimpleTestCase.assertRedirects(response, reverse('pledges:home'))
However, I am getting the following error:
SimpleTestCase.assertRedirects(response, reverse('pledges:home'))
E TypeError: assertRedirects() missing 1 required positional argument: 'expected_url'
Is there any way I can use pytest to verify redirection with Django? Or I should go the way using a class that inherits from SimpleTestCase?
This is an instance method, so it will never work like a class method. You should be able to simply change the line:
SimpleTestCase.assertRedirects(...)
into:
SimpleTestCase().assertRedirects(...)
i.e. we're creating an instance in order to provide a bound method.
I'm not an expert with pytest so probably is not elegant but you can check for the Location header like
test_whatever(self, user):
client = Client()
url = reverse('admin:documents_document_add')
client.force_login(user)
response = client.post(url, {<something>})
# if the document is added correctly we redirect
assert response.status_code == 302
assert response['Location'] == reverse('admin:documents_document_changelist')
I have Pyramid/cornice resource, that requires a ?query=keyword in end of the url. But I don't know how to add this in a pyramid's dummyRequest object. Code works perfectly on browser and I will get correct response when using this url to get stuff: *url*/foo?query=keyword.
My class/resource is defined like this:
#resource(path='/bar/search/foo')
class SearchFooResource(object):
def __init__(self, request):
self.request = request
#view(renderer='json')
def get(self):
#get query string, it's a tuple
req = self.request.GET.items()
#do stuff with req
Now req should contain the all the query string 'stuffs' in a list that contains them as a tuple's, for example: [('query', 'bar'),('query', 'asd')]. But how do I make unittest to this resource? I can't seem to add anything to self.request.GET.items() method. When running unittest req is empty, and I will get this error: AttributeError: 'list' object has no attribute 'items'.
My current unittest:
def test_passing_GetFooBaarResource(self):
request = testing.DummyRequest()
request.GET = [('query', 'keyword')]
info = SearchFooResource.get(SearchFooResource(request))
self.assertEqual(info['foo'], 'baar')
In addition to what #matino has suggested, you can just use a plain dictionary (instead of a list of tuples you tried).
def test_passing_GetFooBaarResource(self):
request = testing.DummyRequest()
request.GET = {'query': 'keyword'}
info = SearchShowResource.get(SearchShowResource(request))
self.assertEqual(info['foo'], 'baar')
This will work in uncomplicated cases where you don't have multiple parameters with the same name (/someurl?name=foo&name=baz&name=bar).
If you need to test those more complicated queries you can replace your DummyRequest's GET attribute with a WebOb MultiDict
from webob.multidict import MultiDict
def test_passing_GetFooBaarResource(self):
request = testing.DummyRequest()
request.GET = MultiDict([('query', 'foo'), ('query', 'bar'), ('query', 'baz')])
info = SearchShowResource.get(SearchShowResource(request))
self.assertEqual(info['foo'], 'baar')
Then, normally, in your actual view method, if you need to handle multiple parameters with the same name you use request.GET.getall('query') which should return ['foo', 'bar', 'baz'].
In simpler cases you can just use request.GET['query'] or request.GET.get('query', 'default'). I mean, your use of request.GET.items() is a bit unusual...
According to the docs I think you need to pass it as params argument (not tested):
request = testing.DummyRequest(params={'query': 'keyword'})
I am attempting to build unit tests and have been using mock, However upon using two patch statements, I was not able to set the proper return values.
#patch('pulleffect.lib.google.gcal_helper.validate_and_refresh_creds')
#patch('pulleffect.lib.google.gcal_helper.get_google_creds')
def test_get_calendar_list_for_gcalhelper_without_credentials(self,
mock_get_google_creds,
mock_validate_and_refresh_creds):
mock_validate_and_refresh_creds = "redirect"
mock_get_google_creds = "credentials"
credentials = pulleffect.lib.google.gcal_helper.get_calendar_list("name","widget")
assert b'redirect' in credentials
however the assert fails and instead of the expected string redirect I instead get
<MagicMock name = "validate_and_refresh_creds() id = 14054613955344>
I was wondering what is necessary to have redirect returned instead. I have not encountered this issue when only patching a single method.
I was able to fix the issue of
<MagicMock name = "foo()" id = number>
incorrectly appearing by replacing my earlier code with:
from mock import MagicMock
def test_get_calendar_list_for_gcalhelper_without_credentials(self):
rtn = { "redirect": "/gcal/authenticate"}
pulleffect.lib.google.gcal_helper.validate_and_refresh_creds = MagicMock(name = "sup", return_value = rtn)
pulleffect.lib.google.gcal_helper.get_google_creds = MagicMock(name = "sup2", return_value = "redirect")
credentials = pulleffect.lib.google.gcal_helper.get_calendar_list("name","widget")
assert b'redirect' in credentials
this allowed the return values to be properly set.
mock_get_google_creds and mock_validate_and_refresh_creds created with patch decorator are ordinary mock objects (Mock or MagicMock). Direct assignment is not the correct way to set return values. Use return_value attribute:
mock_validate_and_refresh_creds.return_value = "redirect"
Also you can set it during patching:
patch takes arbitrary keyword arguments. These will be passed to the
Mock (or new_callable) on construction.
#patch('pulleffect.lib.google.gcal_helper.get_google_creds', return_value="redirect")
I recommend you to use this solution. You should move your functions to helper class and instead static methods user class methods, because it's possible to mock class in this way.
class GCallHelper(object):
#classmethond
def validate_and_refresh(cls):
...
return result
def test_get_calendar_list_for_gcalhelper_without_credentials(self):
with patch('pulleffect.lib.google.gcal_helper') as mocked_gcal:
mocked_gcal.return_value.validate_and_refresh_creds.return_value = 'redirect'
mocked_gcal.return_value.get_google_creds.return_value = 'credentials'
credentials = pulleffect.lib.google.gcal_helper.get_calendar_list("name","widget")
assert b'redirect' in credentials
p.s. And you forgot 'return_value' in your example.
I want to make a site that makes highcharts. However, on the backend, I want to confirm that the json I am getting back is a valid highchart. I would like to do this with some kind of decorator, eg,
#app.route('/this_is_a_chart')
#chart
def make_chart():
return jsonify({
# some generated chart data
})
then it would catch that this should be a highchart, and test that it is valid. How is this possible in flask?
The decorator would look like this, but you need to tell us how to validate the json and how exactly you want to handle the error. This decorator throws an exception.
def chart( func ):
def r():
json = func()
if not_valid_json( json ):
raise "NotAHighChart"
return json
return r