So I was following this youtube video:
https://youtu.be/6tNS--WetLI?t=1973
When I replicate what is done in the video, my test fails:
Class:
import requests
class JetpackRebooter:
#staticmethod
def reboot_jetpack(secure_token):
response = requests.post('http://my.jetpack/restarting/reboot/', data={'gSecureToken' : secure_token})
return response.status_code
Unit test:
from src.jetpack_rebooter import JetpackRebooter
from unittest.mock import patch
def test_reboot_jetpack():
secure_token = 'ca26bacf85c6d69d0bdaa3ff07df3cc4118abf45'
with patch("jetpack_rebooter.requests.post") as mocked_post:
mocked_post.return_value.status_code = 200
assert JetpackRebooter.reboot_jetpack(secure_token) == 200
However, changing patch("jetpack_rebooter.requests.post") to patch("requests.post") causes the test to pass. Why is this?
patch("jetpack_rebooter.requests.post") does not work because the module's full import path is not jetpack_rebooter, but src.jetpack_rebooter.
Because of that, patch("src.jetpack_rebooter.requests.post") would work.
However, as src.jetpack_rebooter.requests points to the requests module object, there is no difference between patch("src.jetpack_rebooter.requests.post") and patch("requests.post"); but there would be a difference between patch("src.jetpack_rebooter.requests") and patch("requests").
I have following function that I want to write unit test for
def get_user_data(user, session):
'''Given a github user, gets it data'''
url = f'https://api.github.com/users/{user}'
response = session.get(url)
json_response = response.json()
return json_response["login"]
This is how I am testing the above function
from unittest.mock import Mock, MagicMock
from requests import Session
response_payload = {"login": "agrawalo"}
fake_session = MagicMock(spec=Session)
fake_session.get.return_value.get_json.return_value = response_payload
If you notice the code above, instead of mocking "json" method on a response object I have created a mock for "get_json" method. This ends up creating get_json method on a response object. And now when I call
get_user_data("agrawalo", fake_session)
I get
<MagicMock name='mock.get().json().__getitem__()' id='139783470311800'>
Whereas I expect the above unittest to fail with NoAttributeError.
In python 3.8 there is a way to fix above problem using "seal" from unitest.mock.
# from python 3.8
from unittest.mock import seal
seal(fake_session)
get_user_data("agrawalo", fake_session)
How can I do this in python 2.7?
I am trying to use DocumentConversionV1 function of watson_developer_cloud API on python , However the response in my case comes only as "<"Response 200">".
import sys
import os as o
import json
import codecs
from watson_developer_cloud import DocumentConversionV1
document_conversion = DocumentConversionV1(
username="873512ac-dcf7-4365-a01d-7dec438d5720",
password="bvhXbdaHtYgw",
version='2016-02-10',
url= "https://gateway.watsonplatform.net/document-conversion/api",
)
config = {
'conversion_target': 'NORMALIZED_TEXT',
}
i = "v.docx"
with open((i),'rb') as doc:
res = document_conversion.convert_document(document = doc , config = config)
print(res)
First and foremost, delete your service credentials and recreate them through Bluemix. (Posting them on a public forum is usually a bad idea.) ;o)
Now, to actually answer the question... You want to get the content of the response. Right now, you're printing the response itself. Try
print(res.content)
See https://github.com/watson-developer-cloud/python-sdk/blob/master/examples/document_conversion_v1.py#L16
Below is a code snippet of my api.py module
# -*- coding: utf-8 -*-
from urllib2 import urlopen
from urllib2 import Request
class API:
def call_api(self, url, post_data=None, header=None):
is_post_request = True if (post_data and header) else False
response = None
try:
if is_post_request:
url = Request(url = url, data = post_data, headers = header)
# Calling api
api_response = urlopen(url)
response = api_response.read()
except Exception as err:
response = err
return response
I am trying to mock urllib2.urlopen in unittest of above module. I have written
# -*- coding: utf-8 -*-
# test_api.py
from unittest import TestCase
import mock
from api import API
class TestAPI(TestCase):
#mock.patch('urllib2.Request')
#mock.patch('urllib2.urlopen')
def test_call_api(self, urlopen, Request):
urlopen.read.return_value = 'mocked'
Request.get_host.return_value = 'google.com'
Request.type.return_value = 'https'
Request.data = {}
_api = API()
assert _api.call_api('https://google.com') == 'mocked'
After I run the unittest, I get an exception
<urlopen error unknown url type: <MagicMock name='Request().get_type()' id='159846220'>>
What am I missing? Please help me out.
You are patching the wrong things: take a look to Where to patch.
In api.py by
from urllib2 import urlopen
from urllib2 import Request
you create a local reference to urlopen and Request in your file. By mock.patch('urllib2.urlopen') you are patching the original reference and leave the api.py's one untouched.
So, replace your patches by
#mock.patch('api.Request')
#mock.patch('api.urlopen')
should fix your issue.... but is not enough.
In your test case api.Request are not used but urllib2.urlopen() create a Request by use the patched version: that is why Request().get_type() is a MagicMock.
For a complete fix you should change your test at all. First the code:
#mock.patch('api.urlopen', autospec=True)
def test_call_api(self, urlopen):
urlopen.return_value.read.return_value = 'mocked'
_api = API()
self.assertEqual(_api.call_api('https://google.com'), 'mocked')
urlopen.assert_called_with('https://google.com')
Now the clarification... In your test you don't call Request() because you pass just the first parameter, so I've removed useless patch. Moreover you are patching urlopen function and not urlopen object, that means the read() method you want mocked is a method of the object return by urlopen() call.
Finally I add a check on urlopen call and autospec=True that is always a good practice.
I am writing an application that performs REST operations using Kenneth Reitz's requests library and I'm struggling to find a nice way to unit test these applications, because requests provides its methods via module-level methods.
What I want is the ability to synthesize the conversation between the two sides; provide a series of request assertions and responses.
It is in fact a little strange that the library has a blank page about end-user unit testing, while targeting user-friendliness and ease of use. There's however an easy-to-use library by Dropbox, unsurprisingly called responses. Here is its intro post. It says they've failed to employ httpretty, while stating no reason of the fail, and written a library with similar API.
import unittest
import requests
import responses
class TestCase(unittest.TestCase):
#responses.activate
def testExample(self):
responses.add(**{
'method' : responses.GET,
'url' : 'http://example.com/api/123',
'body' : '{"error": "reason"}',
'status' : 404,
'content_type' : 'application/json',
'adding_headers' : {'X-Foo': 'Bar'}
})
response = requests.get('http://example.com/api/123')
self.assertEqual({'error': 'reason'}, response.json())
self.assertEqual(404, response.status_code)
If you use specifically requests try httmock. It's wonderfully simple and elegant:
from httmock import urlmatch, HTTMock
import requests
# define matcher:
#urlmatch(netloc=r'(.*\.)?google\.com$')
def google_mock(url, request):
return 'Feeling lucky, punk?'
# open context to patch
with HTTMock(google_mock):
# call requests
r = requests.get('http://google.com/')
print r.content # 'Feeling lucky, punk?'
If you want something more generic (e.g. to mock any library making http calls) go for httpretty.
Almost as elegant:
import requests
import httpretty
#httpretty.activate
def test_one():
# define your patch:
httpretty.register_uri(httpretty.GET, "http://yipit.com/",
body="Find the best daily deals")
# use!
response = requests.get('http://yipit.com')
assert response.text == "Find the best daily deals"
HTTPretty is far more feature-rich - it offers also mocking status code, streaming responses, rotating responses, dynamic responses (with a callback).
You could use a mocking library such as Mocker to intercept the calls to the requests library and return specified results.
As a very simple example, consider this class which uses the requests library:
class MyReq(object):
def doSomething(self):
r = requests.get('https://api.github.com', auth=('user', 'pass'))
return r.headers['content-type']
Here's a unit test that intercepts the call to requests.get and returns a specified result for testing:
import unittest
import requests
import myreq
from mocker import Mocker, MockerTestCase
class MyReqTests(MockerTestCase):
def testSomething(self):
# Create a mock result for the requests.get call
result = self.mocker.mock()
result.headers
self.mocker.result({'content-type': 'mytest/pass'})
# Use mocker to intercept the call to requests.get
myget = self.mocker.replace("requests.get")
myget('https://api.github.com', auth=('user', 'pass'))
self.mocker.result(result)
self.mocker.replay()
# Now execute my code
r = myreq.MyReq()
v = r.doSomething()
# and verify the results
self.assertEqual(v, 'mytest/pass')
self.mocker.verify()
if __name__ == '__main__':
unittest.main()
When I run this unit test I get the following result:
.
----------------------------------------------------------------------
Ran 1 test in 0.004s
OK
Missing from these answers is requests-mock.
From their page:
>>> import requests
>>> import requests_mock
As a context manager:
>>> with requests_mock.mock() as m:
... m.get('http://test.com', text='data')
... requests.get('http://test.com').text
...
'data'
Or as a decorator:
>>> #requests_mock.mock()
... def test_func(m):
... m.get('http://test.com', text='data')
... return requests.get('http://test.com').text
...
>>> test_func()
'data'
using mocker like in srgerg's answer:
def replacer(method, endpoint, json_string):
from mocker import Mocker, ANY, CONTAINS
mocker = Mocker()
result = mocker.mock()
result.json()
mocker.count(1, None)
mocker.result(json_string)
replacement = mocker.replace("requests." + method)
replacement(CONTAINS(endpoint), params=ANY)
self.mocker.result(result)
self.mocker.replay()
For the requests library, this would intercept the request by method and endpoint you're hitting and replace the .json() on the response with the json_string passed in.
If you break out your response handler/parser into a separate function, you can work with requests.Response objects directly, without needing to mock the client-server interaction.
Code under test
from xml.dom import minidom
from requests.models import Response
def function_under_test(s3_response: Response):
doc = minidom.parseString(s3_response.text)
return (
s3_response.status_code,
doc.getElementsByTagName('Code').item(0).firstChild.data,
)
Test code
import unittest
from io import BytesIO
class Test(unittest.TestCase):
def test_it(self):
s3_response = Response()
s3_response.status_code = 404
s3_response.raw = BytesIO(b"""<?xml version="1.0" encoding="UTF-8"?>
<Error>
<Code>NoSuchKey</Code>
<Message>The resource you requested does not exist</Message>
<Resource>/mybucket/myfoto.jpg</Resource>
<RequestId>4442587FB7D0A2F9</RequestId>
</Error>
""")
parsed_response = function_under_test(s3_response)
self.assertEqual(404, parsed_response[0])
self.assertEqual("NoSuchKey", parsed_response[1])
There's a library for this, if you want to write your test server with Flask: requests-flask-adaptor
You just have to be careful with the order of imports when monkeypatching.