I have the following flask app.
# app.py
from flask import Flask, request
from predict import query_sku
app = Flask(__name__)
#app.route("/predict", methods=["POST"])
def predict():
content = request.json
max_results = content["resultSize"]
input_sku_list = content["sku"]
skus = query_sku(input_sku_list, max_results)
return {"sku": skus}
if __name__ == "__main__":
app.run()
I wrote a unit-test for it using pytest and tried to mock the query_sku function using unittest.mock.
import sys
from unittest.mock import Mock
import pytest
import app
def test_api_mocked_model():
sys.modules["predict"] = Mock()
from predict import query_sku
query_sku.return_value = "dummy"
with app.app.test_client() as client:
response = client.post('/predict', json={"resultSize":10,"sku": "x"}).json
assert response == {"sku": "dummy"}
del sys.modules['predict']
But I was unable to mock that function within the request. It just gave the following assertion error.
> assert response == {"sku": "dummy"}
E AssertionError: assert None == {'sku': 'dummy'}
E +None
E -{'sku': 'dummy'}
tests/unit_tests/test_api.py:34: AssertionError
How can I get it to work?
[EDIT]
I added in the query_sku function below. Intentionally return a value that is different from the Mock function return_value.
# predict.py
def query_sku(input_sku_list, topn):
return "actual function"
But the unit-test is still querying from the actual function, as shown below.
assert response == {"sku": "dummy"}
E AssertionError: assert {'sku': 'actual function'} == {'sku': 'dummy'}
E Differing items:
E {'sku': 'actual function'} != {'sku': 'dummy'}
from unittest.mock import MagicMock
def test_api_mocked_model():
## sys.modules["predict"] = Mock() ## why sys module ?
from predict import query_sku
query_sku = MagicMock(return_value="dummy") # mock directly
with app.app.test_client() as client:
response = client.post('/predict', json={"resultSize":10,"sku": "x"}).json
assert response == {"sku": "dummy"}
del sys.modules['predict']
Could you try this code ?
Actually, the answer above did not work for me. Rather than using MagicMock, I used #mock.patch and that worked. And it is less complicated too. For example, in your app.py, you can have an API endpoint that you want to stub out with a mock:
app.py
def fetch_nodes(usr, passwd, hostname, node_name):
sftp_connection = get_sftp_connection(usr, passwd, hostname)
nodes = sftp_connection.listdir_attr(node_name)
return nodes
Now in test_app.py, you can stub it out like this:
test_app.py
from app import app
import unittest
from unittest import mock
class FlaskTest(unittest.TestCase):
#mock.patch('app.fetch_nodes')
def test_list_child_nodes(self, mocked):
tester = app.test_client(self)
mocked.return_value = []
response = tester.post("/api/listchildnodes", json={
'usr': 'test',
'jwt': 'test',
'node_name': 'test',
})
statuscode = response.status_code
self.assertEqual(statuscode, 200)
if __name__ == "__main__":
unittest.main()
The important thing is the definition of mock.patch('app.fetch_nodes') which references the fetch_nodes function in app.py and secondly it is passed to the test function as "mocked". Then we set the return value of "mocked". And finally call the flask endpoint. Now the flask endpoint will use the mock instead of actually hitting an sftp server.
Related
I'm using a 3rd party library "mailjet" to send email.
Here's the doc: https://dev.mailjet.com/email/guides/getting-started/#prerequisites
This is the method in send_email.py that I want to test:
from mailjet_rest import Client
def send_email(email_content):
client = Client(auth=(api_key, api_secret))
response = client.send.create(data=email_content)
if response.status_code != 200:
// raise the error
else:
print(response.status_code)
return response.status_code
...
I wanted to mock the status code. This is what I tried in test_send_email.py:
from unittest.mock import MagicMock, patch
import unittest
import send_email
class TestClass(unittest.TestCase):
#patch("send_email.Client")
def test_send_email(self, _mock):
email_content = {...}
_mock.return_value.status_code = 200
response = send_email(email_content)
// assert response == 200
When I run the test, it didn't print out the status code, it prints:
<MagicMock name='Client().send.create().status_code' id='140640470579328'>
How can I mock the status_code in the test? Thanks in advance!!!!!
You are putting the attribute in the wrong place.
You mock the Client, which is correct, and put something in its return_value, which is also good.
However, the line
response = client.send.create(data=email_content)
applies on the return_value of your mock (client), the send attribute and its create method.
So what you need is to set the return_value.status_code of this create method.
class TestClass(unittest.TestCase):
#patch("send_email.Client")
def test_send_email(self, mock_client):
email_content = {...}
# this puts 200 in Client().status_code
# mock_client.return_value.status_code = 200
# that puts 200 in Client().send.create().status_code
mock_client.return_value.send.create.return_value.status_code = 20
response = send_email(email_content)
assert response == 200
I have the following service with a post request:
import requests
class Attt():
def get_req(self):
pload = {'username': 'Olivia', 'password': '123'}
r = requests.post('https://httpbin.org/post', data=pload)
print(r.text)
print(r.content)
obj=Attt()
obj.get_req()
I would like to mock the request so that I can create the needed response of the service.
I have written a test like below:
import unittest
from unittest.mock import patch
from mock_tutorial.attt import Attt
import pandas as pd
class TestMockService(unittest.TestCase):
def test_mock(self):
fake_json = [{'test': "mock"}]
with patch('mock_tutorial.attt.requests.post') as mock_get:
mock_get.return_value.status_code = 200
mock_get.return_value.json.return_value = fake_json
obj = Attt()
response = obj.get_req()
print('response json', response.json())
self.assertEqual(response.status_code, 200)
self.assertEqual(response.json(), fake_json)
if __name__ == "__main__":
unittest.main()
But as the get_req method doesn't return the reesponse, the assertions fail: AttributeError: 'NoneType' object has no attribute 'json'. How can I modify the assertions to check that the response of the get_req method has been mocked correspondingly, to the fake_json variable?
It is because you are not returning anything from Attt.get_req thus it returns None. So in your test, it is like calling None.json(). You should return the response of the request:
...
class Attt():
def get_req(self):
...
return r
...
Also as documented, you should wrap your code in attt.py so that it doesn't get executed when imported:
if __name__ == "__main__":
obj=Attt()
obj.get_req()
This seems fine since this is just for tutorial. But take note that there are already libraries you can use that mocks the requests module such as requests-mock.
Update
If the method wouldn't return anything, then all we can do to check is by adding a spy to the target functionality. But take note that this wouldn't make sense in reality because what we are spying on is a mocked/patched functionality which is requests.post. This would only be good for learning purposes but in reality, better yet not add the test as it doesn't add any value at all. We can use pytest-mock for this purpose:
mock_tutorial/attt.py
import requests
class Attt():
def get_req(self):
pload = {'username': 'Olivia', 'password': '123'}
r = requests.post('https://httpbin.org/post', data=pload)
print(r.text)
print(r.content)
test_attt.py
from unittest.mock import patch
from mock_tutorial import attt
from mock_tutorial.attt import Attt
import pytest
def test_mock(mocker): # Run <pip install pytest-mock>
fake_json = [{'test': "mock"}]
with patch('mock_tutorial.attt.requests.post') as mock_get:
mock_get.return_value.status_code = 200
mock_get.return_value.json.return_value = fake_json
post_spy = mocker.spy(attt.requests, "post")
obj = Attt()
response = obj.get_req()
assert post_spy.spy_return.status_code == 200
assert post_spy.spy_return.json() == fake_json
i have a function in python in athena.py file
#retry(stop_max_attempt_number=10,
wait_exponential_multiplier=300,
wait_exponential_max=1 * 60 * 1000)
def poll_status(_id):
result = client.get_query_execution(QueryExecutionId=_id)
state = result['QueryExecution']['Status']['State']
if state == 'SUCCEEDED':
return result
elif state == 'FAILED':
return result
else:
raise Exception
where client is reference of boto3.client like this:
client = boto3.client('athena', 'us-west-2')
i have written the unit test for this function using unittest in test_athena.py
#mock.patch('boto3.client')
def test_poll_status(self, mock_client):
event1 = {'QueryExecution': {'Status': {'State': 'SUCCEEDED'}}}
instance = mock_client.return_value()
instance.get_query_execution.return_value = event1
result = athena.poll_status('id')
expected_result = event1
self.assertEqual(expected_result, result)
But this fails. I don't know the reason as i wrote test cases for other functions in similar fashion but this one does not work.
"botocore.exceptions.NoCredentialsError: Unable to locate credentials"
This error is thrown
imports in athena.py
import boto3
from retrying import retry
imports in test_athena.py
import unittest
from unittest import mock
Here's a way to make your test pass - or fail fast.
The fail-fast part is because I'm mocking the retry piece.
Then, I'm mocking a specific function of the client object.
I'm attaching a complete file - including the original code, the test, and a 'main'. It passes on my machine.
import boto3
from retrying import retry
import retrying
import unittest
from unittest import mock
client = boto3.client('athena', 'us-west-2')
#retry(stop_max_attempt_number=10,
wait_exponential_multiplier=300,
wait_exponential_max=1 * 60 * 1000)
def poll_status(_id):
result = client.get_query_execution(QueryExecutionId=_id)
state = result['QueryExecution']['Status']['State']
if state == 'SUCCEEDED':
return result
elif state == 'FAILED':
return result
else:
raise Exception
dummy_retry = retrying.Retrying(stop_max_attempt_number = 10, wait_fixed=1)
class MyTests(unittest.TestCase):
#mock.patch('retrying.Retrying', new = lambda **kwargs: dummy_retry)
#mock.patch.object(client, 'get_query_execution')
def test_poll_status(self, mock_client):
event1 = {'QueryExecution': {'Status': {'State': 'SUCCEEDED'}}}
mock_client.return_value = event1
result = poll_status('id')
expected_result = event1
self.assertEqual(expected_result, result)
if __name__ == '__main__':
unittest.main()
Is you system configured with the aws cli. It seems like you might not have configured aws cli with appropriate access key id and secret key.
This question already has answers here:
How can I mock requests and the response?
(20 answers)
Closed 2 years ago.
I'm writing some unit test code and I would like to mock requests module that is being used inside my function:
import requests
def get_employee(id):
resp = requests.get(f'{__BASE_URL}/employee/{id}')
if resp.status_code == 404:
return None
return resp.json()
I've tried to mock it using this code:
def test_get_employee(mocker):
get_request_mock = mocker.patch.object(get_employee, "resp")
print(get_request_mock)
get_request_mock.status_code = 200
get_request_mock.json.return_value = {'name': 'awesome-mock'}
resp = get_employee('random-id')
assert resp == {'name': 'awesome-mock'}
How can I mock requests using mocker? Is possible?
You can use requests-mock (PyPI), there is a fixture for a pytest usage.
For your example:
from correct.package import __BASE_URL
from requests import HTTPError
def test_get_employee(requests_mock):
test_id = 'random-id'
requests_mock.get(f'{__BASE_URL}/employee/{test_id}', json= {'name': 'awesome-mock'})
resp = get_employee('random-id')
assert resp == {'name': 'awesome-mock'}
def test_absent_employee(requests_mock):
test_id = 'does_not_exist'
requests_mock.get(f'{__BASE_URL}/employee/{test_id}', status_code=404)
with pytest.raises(HTTPError):
resp = get_employee(test_id)
This may help
from unittest import TestCase
import requests
import requests_mock
class TestHTTPRequest(TestCase):
def test_context_manager(self):
with requests_mock.Mocker() as mock_request:
mock_request.get("http://123-fake-api.com", text="Hello!")
response = requests.get("http://123-fake-api.com")
assert response.text == "Hello!"
I am new to Mock and am writing a unit test for this function:
# utils.py
import requests
def some_function(user):
payload = {'Email': user.email}
url = 'http://api.example.com'
response = requests.get(url, params=payload)
if response.status_code == 200:
return response.json()
else:
return None
I am using Michael Foord's Mock library as part of my unit test and am having difficulty mocking the response.json() to return a json structure. Here is my unit test:
# tests.py
from .utils import some_function
class UtilsTestCase(unittest.TestCase):
def test_some_function(self):
with patch('utils.requests') as mock_requests:
mock_requests.get.return_value.status_code = 200
mock_requests.get.return_value.content = '{"UserId":"123456"}'
results = some_function(self.user)
self.assertEqual(results['UserId'], '123456')
I have tried numerous combinations of different mock settings after reading the docs with no luck. If I print the results in my unit test it always displays the following instead of the json data structure I want:
<MagicMock name=u'requests.get().json().__getitem__().__getitem__()' id='30315152'>
Thoughts on what I am doing wrong?
Patch json method instead of content. (content is not used in some_function)
Try following code.
import unittest
from mock import Mock, patch
import utils
class UtilsTestCase(unittest.TestCase):
def test_some_function(self):
user = self.user = Mock()
user.email = 'user#example.com'
with patch('utils.requests') as mock_requests:
mock_requests.get.return_value = mock_response = Mock()
mock_response.status_code = 200
mock_response.json.return_value = {"UserId":"123456"}
results = utils.some_function(self.user)
self.assertEqual(results['UserId'], '123456')
Another pattern I like to use that is a little more reusable would be to start the patcher in your unit test's setUp method. It's also important to check that mock request was called with the expected parameters:
class UtilsTestCase(TestCase):
def setUp(self):
self.user = Mock(id=123, email='foo#bar.com')
patcher = patch('utils.requests.get')
self.mock_response = Mock(status_code=200)
self.mock_response.raise_for_status.return_value = None
self.mock_response.json.return_value = {'UserId': self.user.id}
self.mock_request = patcher.start()
self.mock_request.return_value = self.mock_response
def tearDown(self):
self.mock_request.stop()
def test_request(self):
results = utils.some_function(self.user)
self.assertEqual(results['UserId'], 123)
self.mock_request.assert_called_once_with(
'http://api.example.com'
payload={'Email': self.user.email},
)
def test_bad_request(self):
# override defaults and reassign
self.mock_response.status_code = 500
self.mock_request.return_value = self.mock_response
results = utils.some_function(self.user)
self.assertEqual(results, None)
self.mock_request.assert_called_once_with(
'http://api.example.com'
payload={'Email': user.email},
)
Another way that I believe is more clear and straight forward:
import unittest
from mock import Mock, patch
import utils
class UtilsTestCase(unittest.TestCase):
def test_some_function(self):
mock_response = Mock()
mock_response.status_code = 200
mock_response.json.return_value = {"UserId": "123456"}
with patch('utils.requests.get') as mock_requests:
results = utils.some_function(self.user)
self.assertEqual(results['UserId'], '123456')