I need to make several requests, using either get or post method, I want to write the requests into one statement like
response = getattr(requests, method)(url, parameters)
when method == 'get', Above should work as response = getattr(requests, method)(url, params=parameters), when method == 'post', it should work as response = getattr(requests, method)(url, json=parameters)
You can rely on destructuring for something that doesn't look too terribly ugly.
response = getattr(requests, method)(url, **{('params' if method=='get' else 'json'):parameters})
def getattr(method):
if method == "get": return requests.get
elif method == "post": return requests.post
When you call getattr(method) it will return a function object accordingly. Next you can call the returned function object accordingly.
response = getattr("get")(url, params=parameters) translates to response = requests.get(url, params=parameters)
params = xxx
json =yyy
if method == 'GET':
response = getattr(requests, method)(url, parameters = params)
elif method == 'POST':
response = getattr(requests, method)(url, parameters = json)
Related
Please see this function:
class HttpClient:
#staticmethod
def send_request(session: requests.sessions.Session, request_type: HttpRequestType, url: str, data: json, headers: dict):
if request_type == HttpRequestType.GET:
return session.get(url=url, json=data, headers=headers)
elif request_type == HttpRequestType.POST:
return session.post(url=url, json=data, headers=headers)
class HttpRequestType(Enum):
GET = 'Get'
POST = 'Create'
PUT = 'Update'
DELETE = 'Delete'
So instead of use this if-else condition is it possible to pass the post or get type to this function ?
EDIT
url = 'my_url'
data = f'username=my_user&password=my_password&submit=Login'
headers = {"content-type": "application/x-www-form-urlencoded"}
response = send_request(s.post, *[url, data, headers])
def send_request(func_to_use, *args):
return func_to_use(*args)
Usage
response = send_request(s.post, *[url, data, headers])
Sure, you can actually pass the function to use as an argument.
Here's a simplified example:
def send_request(func_to_use, *args):
func_to_use(*args)
send_request(session.get, *['my', 'get', 'args'])
send_request(session.post, *['my', 'post', 'args'])
Should be easy enough to adapt this example to your specific needs.
Another approach, using getattr:
def send_request(session, method, *args):
getattr(session, method)(*args)
send_request(session, 'get', *['my', 'get', 'args'])
send_request(session, 'post', *['my', 'post', 'args'])
How to check if a request mocked by requests_mock added some query parameters to a URL?
I have a function func thats do a HTTP POST on the url with some query string on the URL and I want to check if was called with this query string.
This is my attempt, but fails.
query is a empty string and qs is a empty dict.
I have sure that my func is appending the query string on the request.
with requests_mock.Mocker() as mock:
mock.post(url, text=xml)
func() # This function will call url + query string
history = mock.request_history[0]
assert history.method == "POST" # OK
assert history.query is None # Returns an empty string, AssertionError: assert '' is None
assert history.qs is None # Returns an empty dict, assert {} is None
My func
def credilink():
url = settings["url"]
params = settings["params"]
params["CC"] = query
response = requests.post(url, params=params)
# ...
I tried to reproduce your problem and was unable to...
Here is the code I'm running:
import requests
import requests_mock
url = "http://example.com"
settings = dict(url=url, params=dict(a=1))
query = "some-query"
xml = "some-xml"
def credilink():
url = settings["url"]
params = settings["params"]
params["CC"] = query
response = requests.post(url, params=params)
return response.text
# ...
def test():
with requests_mock.Mocker() as mock:
mock.post(url, text=xml)
data = credilink() # This function will call url + query string
history = mock.request_history[0]
assert history.method == "POST" # OK
assert history.qs == dict(a=['1'], cc=[query])
assert history.query == f"a=1&cc={query}"
assert data == xml
The assertions pass in this snippet.
Maybe it's some version problem? I used requests==2.25.1 and requests-mock==1.8.0.
In my case, the problem was in mock:// URL schema, as it's present in the requests-mock samples
session.get('mock://test.com/path')
But requests library skips query arguments for non "http" URLs. Here is the comment from the source code
# Don't do any URL preparation for non-HTTP schemes like `mailto`,
# `data` etc to work around exceptions from `url_parse`, which
# handles RFC 3986 only.
if ':' in url and not url.lower().startswith('http'):
self.url = url
return
I have looked at How to mock REST API and I have read the answers but I still can't seem to get my head around how I would go about dealing with a method that executes multiple GET and POST requests. Here is some of my code below.
I have a class, UserAliasGroups(). Its __init__() method executes requests.post() to login into the external REST API. I have in my unit test this code to handling the mocking of the login and it works as expected.
#mock.patch('aliases.user_alias_groups.requests.get')
#mock.patch('aliases.user_alias_groups.requests.post')
def test_user_alias_groups_class(self, mock_post, mock_get):
init_response = {
'HID-SessionData': 'token==',
'errmsg': '',
'success': True
}
mock_response = Mock()
mock_response.json.return_value = init_response
mock_response.status_code = status.HTTP_201_CREATED
mock_post.return_value = mock_response
uag = UserAliasGroups(auth_user='TEST_USER.gen',
auth_pass='FakePass',
groups_api_url='https://example.com')
self.assertEqual(uag.headers, {'HID-SessionData': 'token=='})
I also have defined several methods like obtain_request_id(), has_group_been_deleted(), does_group_already_exists() and others. I also define a method called create_user_alias_group() that calls obtain_request_id(), has_group_been_deleted(), does_group_already_exists() and others.
I also have code in my unit test to mock a GET request to the REST API to test my has_group_been_deleted() method that looks like this:
has_group_been_deleted_response = {
'error_code': 404,
'error_message': 'A group with this ID does not exist'
}
mock_response = Mock()
mock_response.json.return_value = has_group_been_deleted_response
mock_response.status_code = status.HTTP_404_NOT_FOUND
mock_get.return_value = mock_response
Now I can get to my question. Below is the pertinent part of my code.
class UserAliasGroups:
def __init__(
self,
auth_user=settings.GENERIC_USER,
auth_pass=settings.GENERIC_PASS,
groups_api_url=settings.GROUPS_API_URL
):
""" __init__() does the login to groups. """
self.auth_user = auth_user
self.auth_pass = auth_pass
self.headers = None
self.groups_api_url = groups_api_url
# Initializes a session with the REST API service. Each login session times out after 5 minutes of inactivity.
self.login_url = f'{self.groups_api_url}/api/login'
response = requests.post(self.login_url, json={}, headers={'Content-type': 'application/json'},
auth=(auth_user, auth_pass))
if response.status_code is not 201:
try:
json = response.json()
except:
json = "Could not decode json."
raise self.UserAliasGroupsException(f"Error: User {self.auth_user}, failed to login into "
f"{self.login_url} {json}")
response_json = response.json()
self.headers = {'HID-SessionData': response_json['HID-SessionData']}
def obtain_request_id(self, request_reason):
payload = {'request_reason': request_reason}
url = f'{self.groups_api_url}/api/v1/session/requests'
response = requests.post(url=url, json=payload, headers=self.headers)
if response.status_code is not status.HTTP_200_OK:
try:
json = response.json()
except:
json = "Could not decode json."
msg = f'obtain_request_id() Error url={url} {response.status_code} {json}.'
raise self.UserAliasGroupsException(msg)
request_id = response.json().get('request_id')
return request_id
def has_group_been_deleted(self, group_name):
url = f'{self.groups_api_url}/api/v1/groups/{group_name}/attributes/RESATTR_GROUP_DELETED_ON'
response = requests.get(url=url, headers=self.headers)
return response.status_code == status.HTTP_200_OK
def does_group_already_exists(self, group_name):
url = f'{self.groups_api_url}/api/v1/groups/{group_name}'
response = requests.get(url=url, headers=self.headers)
if response.status_code is status.HTTP_200_OK:
# check if the group has been "deleted".
return not self.has_group_been_deleted(group_name=group_name)
return False
def create_user_alias_group(
self,
... long list of params omitted for brevity ...
):
if check_exists:
# Check if group already exists or not.
if self.does_group_already_exists(group_name):
msg = f'Cannot create group {group_name}. Group already exists.'
raise self.UserAliasGroupsException(msg)
... more code omitted for brevity ...
My question is how do I write my unit test to deal with multiple calls to requests.post() and request.get() all resulting in different responses in my create_user_alias_group() method?
I want to call create_user_alias_group() in my unit test so I have to figure out how to mock multiple requests.get() and requests.post() calls.
Do I have use multiple decorators like this:
#mock.patch('aliases.user_alias_groups.obtain_request_id.requests.post')
#mock.patch('aliases.user_alias_groups.does_group_already_exists.requests.get')
#mock.patch('aliases.user_alias_groups.has_group_been_deleted.requests.get')
def test_user_alias_groups_class(self, mock_post, mock_get):
...
?
Thanks for looking my long question :)
You can use mock.side_effect which takes an iterable. Then different calls will return different values:
mock = Mock()
mock.side_effect = ['a', 'b', 'c']
This way the first call to mock returns "a", then the next one "b" and so on. (In your case, you'll set mock_get.side_effect).
I have a code below which setups cookie and then adds this cookie to the response with set_cookies function. However, even though I return a response I received the following error:
ValueError: View function did not return a response
My code is simply this:
def login():
if request.method == "POST":
timestamp = str(int(time.time()))
cookie = timestamp+'user'
cookie = base64.b64encode(cookie.encode('utf-8')).decode('utf-8')
resp = make_response()
resp = resp.set_cookie("LoginCookie",cookie)
return resp
response.set_cookie is an in-place operation. This means that if you set something to it's return value, it will be None, which is the default return value (functions with no return return None).
What should be used instead is:
resp.set_cookie("LoginCookie",cookie)
my deorator function...
def validate_captcha(view):
'''Decorator to validate a captcha based on settings'''
def failure():
return HttpResponse('You need to complete the captcha, please refresh and try again')
if request.method == 'POST':
url = "https://www.google.com/recaptcha/api/siteverify"
values = {
'secret': settings.GOOGLE_RECAPTCHA_SECRET_KEY,
'response': request.POST.get(u'g-recaptcha-response', None),
'remoteip': request.META.get("REMOTE_ADDR", None),
}
data = urllib.urlencode(values)
req = urllib2.Request(url, data)
response = urllib2.urlopen(req)
result = json.loads(response.read())
# result["success"] will be True on a success
if result["success"]:
return view
else:
return fail
return fail
and then my view...
#validate_captcha
def sendemail(request):
...
I could put the request in the decorator args, but then it is undefined when i put it in the view args. I tried calling it a few others ways without success, how would you put it in there?
You need to have a wrapper function:
def validate_captcha(view):
def wrap(request, *args, **kwargs):
if request.method == 'POST':
url = "https://www.google.com/recaptcha/api/siteverify"
values = {
'secret': settings.GOOGLE_RECAPTCHA_SECRET_KEY,
'response': request.POST.get(u'g-recaptcha-response', None),
'remoteip': request.META.get("REMOTE_ADDR", None),
}
data = urllib.urlencode(values)
req = urllib2.Request(url, data)
response = urllib2.urlopen(req)
result = json.loads(response.read())
# result["success"] will be True on a success
if result["success"]:
return view
else:
return fail
return fail
return wrap
Make sure study this awesome and quite detailed overview on decorators in Python (I personally think, it is one of the best SO answers ever):
https://stackoverflow.com/a/1594484/771848