How to mock a function inside a http request? - python

I am using pytest, I have a test like:
def test_fun1(client):
rsp = client.get(url)
Inside this get url which in my flask server, their is a function fun2(),
# restful api
def get():
res1 = fun2()
# do thing on res1
return res2
I want mock this fun2(), in my pytest test_fun1, with patch.object() and Mock()not work, any suggestion? Thanks!

Related

Is there a way to convert local flask http request to a function call?

Currently, in our system, we are calling the endpoints even in the same flask application by a HTTP request. All the requests is called through a make_request wrapper method as shown below:
def make_request(url, body, http_type="get"):
http_fn = getattr(requests, http_type)
response = http_fn(url, headers=headers, json=body)
return response.status_code, response
Hence I'm trying to convert all local requests within the same flask application to a direct method call so that any endpoint within the same flask application is called this way:
def make_request(url, body, http_type="get"):
# Figure out If its local request call the function of the endpoint and construct the response
# If not make an http request
return response.status_code, response
EDIT: Tried searching in the url_map to find the method associated with the endpoint but the function returned in not in a callabale state. Any points on how we can call the method from here?
for rule in current_app.url_map.iter_rules():
if my_url in rule.rule:
endpoint = rule.endpoint
for key, view in current_app.view_functions.items():
if key == endpoint:
# Found the view function, need to know how to call
# the right method( GET, POST etc)
view contains the following:
{
'view_class': <class 'endpoints.attribute_endpoints.AttributeEndpoint'>,
'methods': {'GET', 'PUT', 'POST'}, 'provide_automatic_options': None, '__wrapped__': <function View.as_view.<locals>.view at 0x10c9190d0>}
If I understand correctly, what you're trying to achieve is calling a flask endpoint internally without going over http. Look at the solution below and let me know if it is does what you want.
Old Code:
#app.route('/someRoute', methods=['GET'])
def some_route_function():
json_object = request.get_json()
my_number = json_object['myNumber']
my_number = my_number**2
return jsonify(my_number=my_number)
New Code:
def square_number_func(number):
return number**2
#app.route('/someRoute', methods=['GET'])
def some_route_function():
json_object = request.get_json()
my_number = json_object['myNumber']
my_number = square_number_func(my_number)
return jsonify(my_number=my_number)
def my_non_flask_function():
my_number = 17
my_number = square_number_func(my_number)
This way you get the functionality you need without having to rely on Flask's request object, nor having to call flask via http.
Edit: If you need to figure out if it's an internal flask function then you compare it against a list of functions in your local global scope or in your flask app, as it does store your routes. You can even store the function parameters so you know what to call. Finally, you can map each endpoint to another function if you want inside a dictionary or something, such as {some_route_function: square_number_func} so that you can tell which function to substitute for the http call.

Mock streaming API in python for unit test

I have an async function that calls a streaming api. What is the best way to write unit test for this function? The api response has to be mocked.
I tried with aiounittest and used mock from unittest. But this calls the actual api instead of getting the mocked response. Also tried with pytest.mark.asyncio annotation, but this kept giving me the error - coroutine was never awaited. I have verified that pytest-asyncio has been installed.
I am using VS Code and Python 3.6.6
Here is the relevant code snippet:
async def method1():
response = requests.get(url=url, params=params, stream=True)
for data in response.iter_lines():
# processing logic here
yield data
Pasting some of the tests I tried.
def mocked_get(*args, **kwargs):
#implementation of mock
class TestClass (unittest.TestCase):
#patch("requests.get", side_effect=mocked_get)
async def test_method (self, mock_requests):
resp = []
async for data in method1:
resp.append (data)
#Also tried await method1
assert resp
Also tried with class TestClass (aiounittest.AsyncTestCase):
Use asynctest instead of aiounittest.
Replace unittest.TestCase with asynctest.TestCase.
Replace from unittest.mock import patch with from asynctest.mock import patch.
async for data in method1: should be async for data in method1():.
import asynctest
from asynctest.mock import patch
class TestClass(asynctest.TestCase):
#patch("requests.get", side_effect=mocked_get)
async def test_method(self, mock_requests):
resp = []
async for data in method1():
resp.append(data)
assert resp

How to test functions that request data from external endpoints in django

I am trying to test my functions on my django api that perform external requests to external api. How can
i test the following scenarios: success, failed, and exceptions like timeout
The following is a simplified functionality
def get_quote(*args):
# log request
try:
response = requests.post(url, json=data)
# parse this response
except:
# log file :)
finally:
# log_response(...)
return parsed_response or None
None: response can be success, failed, can timeout. I want to test those kind of scenarios
You can mock the result of calling the external API and set an expected return value in the test function:
from unittest.mock import patch
from django.test import TestCase
class ExternalAPITests(TestCase):
#patch("requests.post")
def test_get_quote(self, mock):
mock.return_value = "predetermined external result"
self.assertEquals("expected return value", get_quote())
You can use the responses package - https://pypi.org/project/responses/
import unittest
import responses
from your_package import get_quote
class TestPackage(unittest.TestCase):
#responses.activate
def test_get_quote(self):
url = "http://some_fake_url.com"
responses.add(responses.POST, url, json={"test": "ok"}, status=200)
self.assertDictEqual({"test": "ok"}, get_quote(url))
#responses.activate
def test_get_quote_with_exception(self):
url = "http://some_fake_url.com"
responses.add(responses.POST, url, body=Exception('...'))
with self.assertRaises(Exception):
get_quote(url)

How to get dynamic path params from route in aiohttp when mocking the request?

Using the below route definition, I am trying to extract the book_id out of the URL in aiohttp.
from aiohttp import web
routes = web.RouteTableDef()
#routes.get('/books/{book_id}')
async def get_book_pages(request: web.Request) -> web.Response:
book_id = request.match_info.get('book_id', None)
return web.json_response({'book_id': book_id})
Below is the test (using pytest) I have written
import asynctest
import pytest
import json
async def test_get_book() -> None:
request = make_mocked_request('GET', '/books/1')
response = await get_book(request)
assert 200 == response.status
body = json.loads(response.body)
assert 1 == body['book_id']
Test Result:
None != 1
Expected :1
Actual :None
Outside of the tests, when I run a request to /books/1 the response is {'book_id': 1}
What is the correct way to retrieve dynamic values from the path in aiohttp when mocking the request?
make_mocked_request() knows nothing about an application and its routes.
To pass dynamic info you need to provide a custom match_info object:
async def test_get_book() -> None:
request = make_mocked_request('GET', '/books/1',
match_info={'book_id': '1'})
response = await get_book(request)
assert 200 == response.status
body = json.loads(response.body)
assert 1 == body['book_id']
P.S.
In general, I want to warn about mocks over-usage. Usually, functional testing with aiohttp_client is easier to read and maintain.
I prefer mocking for really hard-to-rest things like network errors emulation.
Otherwise your tests do test your own mocks, not a real code.

how to make a fake post request using unittest module with cookies and data?

I have an endpoint like this:-
#app.route('/name', methods=['POST'])
#limiter.limit("2000/day;300/hour;5/minute", key_func = get_uid_from_request)
#authenticate
def post(user):
How do I make a fake post request using unittest module?
Well, you can make an actual post request
import requests
def test_post():
resp = requests.post('http://localhost/name',
data={'arg': 'value'},
cookies={'from-my': 'browser'})
assert resp.status_code == 200
I would recommend using py.test instead of unittest, but if you must use unittest
class TestPost(unittest.TestCase):
def test_post(self):
resp = requests.post('http://localhost/name')
self.assertEqual(resp.status_code, 200)
You could do like already suggested, and do an acceptance test against a running test instance with real post requests.
You can also check out Flasks documentation for testing at http://flask.pocoo.org/docs/0.10/testing/ which demonstrates how to do unit tests in which you can mock the incoming requests and test results.

Categories