I have a code which I am trying to test using Mock library/python-django. To give brief summary of my application is:
(PHASE I): Client use my app exposed API. The mapped API function make a HTTP Connection request to 3rd party API ( Tropo which are CaaS provider)
(PHASE II): Tropo server(3rd party) respond back with some url back to my server which I mapped to function which send another request to Tropo Server which on their side, make call() to phonenumbers.
I used Django test client by just using my API which does run but the problem is it also reply on Tropo to make real call to the numbers I give in. So I thought to use mock() library but I have very little knowledge of it.
What I did is, I see what response I get after first phase from Tropo hard coded it in variable input and I also have expected_output variable which is also hardcoded seeing the output after second phase.
But I want to architect my test framework properly in which I can mock the whole tropo library in my testing environment and all the request go to this fake library and not the real tropo. But not changing the code.
Any ideas or suggestion. Please bare me as I am developer and this is something I try to do for testing.
Since I am not getting any response I am trying to give more details of what exactly I am stuck in:
Let say there this snippet of my code in one function...
conn = httplib.HTTPConnection('Some_External_URI')
headers = {"accept": "application/json", "Content-type":"application/json"}
params = ''
conn.request('POST', data, params, headers)
response = conn.getresponse()
payload = response.read()
How I can mock this particular connection request?
I was able to reach at some level of testing by mocking class in my code.
test.py
from mock import patch, MagicMock
from tropo.conferencing import TropoConferencing
#patch.object(TropoConferencing, 'sendTriggerCallRequest')
def test_ConferenceCreation(self, sendTriggerCallRequest):
response_conference = self._createConference(sendTriggerCallRequest)
self.assertContains(response_conference, 200)
def _createConference(self, sendTriggerCallRequest):
self._twoParticipants_PhaseI(sendTriggerCallRequest)
response_conference = self.client.post(self.const.create_conferenceApi , {'title':self.const.title,'participants':self.const.participants})
obj = json.loads(response_conference.content)
self.conf_id = obj['details']['conference_id']
try:
conference_id = Conference.objects.get(conferenceId=self.conf_id)
except Conference.DoesNotExist:
print 'Conference not found'
# PHASE II
self._twoParticipants_PhaseII()
return response_conference
def _twoParticipants_PhaseI(self, sendTriggerCallRequest):
list_of_return_values= [{'json': {'session_id': 'e85ea1229f2dd02c7d7534c2a4392b32', 'address': u'xxxxxxxxx'}, 'response': True},
{'json': {'session_id': 'e72bf728d4de2aa039f39843097de14f', 'address': u'xxxxxxxx'}, 'response': True}
]
def side_effect(*args, **kwargs):
return list_of_return_values.pop()
sendTriggerCallRequest.side_effect = side_effect
def _twoParticipants_PhaseII(self):
input = {"session":{"id":"e72bf728d4de2aa039f39843097de14f","accountId":"xxxxx","timestamp":"2013-01-07T18:32:20.905Z","userType":"NONE","initialText":'null',"callId":'null',"parameters":{"phonenumber":"xxxxxxx","action":"create","conference_id":str(self.conf_id),"format":"form"}}}
expectedOutput = '{"tropo": [{"call": {"to": "xxxxxxx", "allowSignals": "exit", "from": "xxxxxxx", "timeout": 60}}, {"ask": {"name": "join_conference", "say": {"value": "Please press one to join conference"}, "choices": {"terminator": "*", "value": "1", "mode": "dtmf"}, "attempts": 1, "timeout": 5, "voice": "Susan"}}, {"on": {"event": "mute", "next": "' + self.const.muteEvent+ str(self.conf_id) + '/xxxxxx"}}, {"on": {"event": "unmute", "next": "/conference/rest/v1/conference/events/unmute/'+ str(self.conf_id) + '/xxxxxxx"}}, {"on": {"event": "hangup", "next": "'+ str(self.conf_id) + '/xxxxxx"}}, {"on": {"event": "continue", "next": "'+ str(self.conf_id) + '/xxxxxx"}}, {"on": {"event": "exit", "next": "'+ str(self.conf_id) + '/xxxxxx"}}, {"on": {"event": "error", "next": "/conference/rest/v1/conference/events/hangup/'+ str(self.conf_id) + '/xxxxxxx"}}, {"on": {"event": "incomplete", "next": "'+ str(self.conf_id) + '/xxxxxxx"}}, {"say": {"value": ""}}]}'
callbackPayload = json.dumps(input)
request = MagicMock()
request.raw_post_data = callbackPayload
response = call(request)
self.assertEqual(response.content, expectedOutput)
As you can see I am hardcoding the response I get form Tropo and passing onto the function. Please let me know if any QA have better solution to this type of problem
Related
It shows credentials are missing when I try to use the Google Gmail API to send messages. I want to send an email to my other Gmail account using the Google Gmail API.
import sys
import requests
import base64
import sys
from email.mime.text import MIMEText
AccessToken = ""
params = {
"grant_type": "refresh_token",
"client_id": "xxxxxxxxxxxxxxx",
"client_secret": "xxxxxxxxxxxxxxx",
"refresh_token": "xxxxxxxxxxxxxxxxxxxx",
}
authorization_url = "https://www.googleapis.com/oauth2/v4/token"
r = requests.post(authorization_url, data=params)
if r.ok:
AccessToken = str((r.json()['access_token']))
EmailFrom = "Test1#gmail.com"
EmailTo = "test2#gmail.com"
def create_message(sender, to, subject, message_text):
message = MIMEText(message_text, 'html')
message['to'] = to
message['from'] = sender
message['subject'] = subject
raw = base64.urlsafe_b64encode(message.as_bytes())
raw = raw.decode()
body = {'raw': raw}
return body
body = create_message(EmailFrom, EmailTo, "Just wanna Say Waka Waka!", "Waka Waka!")
url = "https://gmail.googleapis.com/gmail/v1/users/me/messages/send"
header = {
'Authorization': 'Bearer ' + AccessToken,
'Content-Type': 'application/json',
'Accept': 'application/json'
}
r = requests.post(
url,
header,
body
)
print("\n")
print(r.text)
print("\n")
Error:
{
"error": {
"code": 401,
"message": "Request is missing required authentication credential. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.",
"errors": [
{
"message": "Login Required.",
"domain": "global",
"reason": "required",
"location": "Authorization",
"locationType": "header"
}
],
"status": "UNAUTHENTICATED",
"details": [
{
"#type": "type.googleapis.com/google.rpc.ErrorInfo",
"reason": "CREDENTIALS_MISSING",
"domain": "googleapis.com",
"metadata": {
"method": "caribou.api.proto.MailboxService.SendMessage",
"service": "gmail.googleapis.com"
}
}
]
}
}
I'm not a google api user, but I've used oauth several times, and your setup is a bit different than what I usually use or what I see from a quick sniff of Google's documention. For example, I use client-creds instead of a refresh token. For what I'm seeing above in your code, no reason to refresh an old token, when you can just mint another one. Compared to yours, usually with oauth, we'll do something like auth=(client_id, client_secret)
Lastly, before you change anything big, when you changed your header to place the AccessToken variable in quotes, did you use an f-string? What is the output of
print(header)
after you have defined it? Is it what you expect? If you didn't format it, it's going to have the variable's name rather than value.
If that's all ok, I'd try to write it according to OAuth standards that I've used several times. Not telling you how to do it, but you could try something like:
def getKey():
url = "https://www.googleapis.com/oauth2/v4/token"
client_id = "*yourgoogleclientid*"
client_secret = "*yourgoogleclientsecret*"
data = {
'grant_type': 'client_credentials'
}
r = requests.post(url, json=data, auth=(client_id, client_secret))
key = r.json()['access_token']
return key
def getWhatever(key, url):
header = {
'Authorization': f'Bearer {key} '
}
params = {
'whatever params': 'you might need'
}
response = requests.get(url, headers=header, params=params)
* parse, process, return, whatever you'd like to do with the response.*
now to use it....
if __name__ == '__main__':
myKey = getKey()
whatImLookingFor = getWhatever(myKey, "*https://google_api_endpoint*")
I've got a little utility function built like this to grab data from another applications API:
# app/utils.py
import json
import requests
from django.conf import settings
def get_future_assignments(user_id):
"""gets a users future assignments list from the API
Arguments:
user_id {int} -- user_id for a User
"""
headers = {
"User-Agent": "Mozilla/5.0",
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
"X-Requested-With": "XMLHttpRequest",
}
api_app = settings.ASSIGNMENTS_API_ROOT_URL # http://project.org/appname/
api_model = "futureassignments/"
api_query = "?user_id=" + str(user_id)
json_response = requests.get(
api_app + api_model + api_query, headers=headers, verify=False
)
return json.loads(json_response.content)
It basically builds the API call and returns the response data - I'd like to test this.
# tests/test_utils.py
import mock
from unittest.mock import patch, Mock
from django.test import TestCase
from app.utils import get_future_assignments
class UtilsTest(TestCase):
def setUp(self):
self.futureassignments = [
{
"id": 342,
"user_id": 18888,
"job": 361,
"location": "1234",
"building": "Building One",
"confirmed_at": None,
"returning": None,
"signature": None,
},
{
"id": 342,
"user_id": 18888,
"job": 361,
"location": "1235",
"building": "Building Two",
"confirmed_at": None,
"returning": None,
"signature": None,
},
]
#patch("app.utils.get_future_assignments")
def test_get_future_assignments_with_multi_assignments(self, mock_gfa):
"""
Test for getting future assignments for a user with mocked API
"""
mock_gfa.return_value = Mock()
# set the json response to what we're expecting
mock_gfa.return_value.json.return_value = self.futureassignments
assignments = get_future_assignments(18888)
self.assertEqual(len(assignments), 2)
It keeps giving me an error that it can't reach the API to get a response (which is expected at the moment - since I'm running it locally and it cannot hit the API)
I'm new to using Mock - so maybe I'm way, way off base here.
Any ideas?
Like you, I am also new to using mock. I believe it's intended to work like this:
#patch("requests.get")
def test_get_future_assignments_with_multi_assignments(self, mock_requests_get):
"""
Test for getting future assignments for a user with mocked API
"""
mock_requests_get.return_value = json.dumps(self.futureassignments)
assignments = get_future_assignments(18888)
self.assertEqual(len(assignments), 2)
Please correct me if I'm wrong!
I'm trying to post events to Google Analytics. It works fine when I do it using the NodeJS code below, but fails when I use the Python code below. Both do return a HTTP 200 and even when posting to the debug URL (https://www.google-analytics.com/debug/collect) Google Analytics returns success details in both cases (see valid: true in the response below). The problem is that when posting from NodeJS the result shows up in the GA website, when posting from Python it never shows up. I did compare the requests for both and have not been able to spot a difference.
{
"hitParsingResult": [ {
"valid": true,
"parserMessage": [ ],
"hit": "/debug/collect?v=1\u0026t=event\u0026tid=XXXXXXX\u0026cid=YYYYYYu0026ec=Slack\u0026ea=SlashCommand\u0026el=whowasat-curl\u0026an=staging.Whereis-Everybody?\u0026aid=staging.whereis-everybody.com"
} ],
"parserMessage": [ {
"messageType": "INFO",
"description": "Found 1 hit in the request."
} ]
}
The NodeJS code is (result does show up in Google Analytics):
'use strict';
var request = require('request');
require('request-debug')(request);
function postEventToGA(category, action, label) {
var options = {
v: '1',
t: 'event',
tid: process.env.GOOGLEANALYTICS_TID,
cid: process.env.GOOGLEANALYTICS_CID,
ec: category,
ea: action,
el: label,
an: process.env.STAGE_INFIX + "appname",
aid: process.env.STAGE_INFIX + "appname"
};
console.log("payload: " + JSON.stringify(options))
request.post({ url: 'https://www.google-analytics.com/collect', form: options }, function (err, response, body) {
console.log(request)
if (err) {
console.log("Failed to post event to Google Analytics, error: " + err);
} else {
if (200 != response.statusCode) {
console.log("Failed to post event to Google Analytics, response code: " + response.statusCode + " error: " + err);
}
}
});
}
postEventToGA("some-category", "some-action", "some-label")
And the Python code is (result does not show up in Google Analytics):
import json
import logging
import os
import requests
LOGGER = logging.getLogger()
LOGGER.setLevel(logging.INFO)
GOOGLEANALYTICS_TID = os.environ["GOOGLEANALYTICS_TID"]
GOOGLEANALYTICS_CID = os.environ["GOOGLEANALYTICS_CID"]
STAGE_INFIX = os.environ["STAGE_INFIX"]
def post_event(category, action, label):
payload = {
"v": "1",
"t": "event",
"tid": GOOGLEANALYTICS_TID,
"cid": GOOGLEANALYTICS_CID,
"ec": category,
"ea": action,
"el": label,
"an": STAGE_INFIX + "appname,
"aid": STAGE_INFIX + "appname",
}
response = requests.post("https://www.google-analytics.com/collect", payload)
print(response.request.method)
print(response.request.path_url)
print(response.request.url)
print(response.request.body)
print(response.request.headers)
print(response.status_code)
print(response.text)
if response.status_code != 200:
LOGGER.warning(
"Got non 200 response code (%s) while posting to GA.", response.status_code
)
post_event("some-category", "some-action", "some-label")
Any idea why the NodeJS post will show up in Google Analytics and the Python post does not?
(while both return a HTTP200)
Did some more testing and discovered that the user agent HTTP header was causing the problem. When I set it to an empty string in the Python code it works. Like this:
headers = {"User-Agent": ""}
response = requests.post(
"https://www.google-analytics.com/collect", payload, headers=headers
)
The documentation at https://developers.google.com/analytics/devguides/collection/protocol/v1/reference does state that the user agent is used, but does not clearly state what the requirements are. "python-requests/2.22.0" (default value by python-requests lib) is apparently not accepted.
When reviewing the abilities of the Python API for Trello and considering its functionalities,
I was searching for a function that allows to add a member to a board - w./o. success.
What I have tried to use is the following:
trello = TrelloAPI(myAPIKey, myToken)
boardID = myBoardID
fields = {"fullName": "Robo Member",
"email" : myMail}
trello.boards.get_membersInvited(board_id = boardID,
fields = fields)
This is how the method implementation looks like:
def get_membersInvited(self, board_id, fields=None):
resp = requests.get("https://trello.com/1/boards/%s/membersInvited" %
(board_id), params=dict(key=self._apikey, token=self._token,
fields=fields), data=None)
resp.raise_for_status()
return json.loads(resp.content)
I end up receiving 404 Client Error URL not found.
Do you have any suggestions on what to adjust?
Maybe, I used the wrong field names (email and fullName)?
Here is a solution for .NET
Found a remedy by myself. Source --> Trello API Board Put Member
Here my own solution:
def invite_new_member(self, fullName, email, boardID):
url = self.baseURL + "boards/" + boardID + "/members"
querystring = {"email": email, "key": apikey,
"token": token}
payload = "{\"fullName\":\"" + fullName + "\"}"
headers = {
'type': "normal",
'content-type': "application/json" }
response = requests.request("PUT", url, data=payload, headers=headers,
params=querystring)
For one of my post requests, I get a 500 response every time I try to run it. When I copy and paste the details in postman, it works fine every time. The python post works fine for other requests that I run, but this one fails every time and I can't work out why.
Has anyone come across this problem before, or can anyone see something that I've done wrong?
json_body = '{"overrides": [], "id": 0, "name": "Rate Sheet 12", "resellerId": 2000001, "currency": "ZAR", "markup": {"id": 0, "method": "Percentage", "operand": 3}, "totalLinkedBands": 0, "totalLinkedAccounts": 0}'
token = 'JWT eyJ0eXA...s'
url = 'http://app01:8084//PriceScheme/CreatePriceScheme'
r = requests.post(url, json.loads(json_body), headers={'Authorization': token})
In Postman it looks as follows:
(POST) http://app01:8084//PriceScheme/CreatePriceScheme
Content-Type - application/json
Authorization - JWT eyJ...
{"overrides": [], "name": "Rate Sheet 1", "currency": "ZAR", "totalLinkedAccounts": 0, "totalLinkedBands": 1, "id": 1, "markup": {"method": "Percentage", "operand": 3.0, "id": 0}, "resellerId": "2009340"}
try as blew
requests.post(url, json = json_body, headers={'Authorization': token})
In Postman , auto using Content-Type - application/json
If using request post json data , should using json=data
json_body='{"parametername":Value}'
resp = requests.post(URL, json_body, auth=('username', 'Pass'))
Solved the issue in my case