Flask AssertionError: 400 != 201 [duplicate] - python

This question already has answers here:
How to send requests with JSON in unit tests
(2 answers)
Closed 4 years ago.
I am trying to write a test case for my post end point, but I seriously do not know what I am doing wrong.
views.py
The user can post a product by typing the name, the price and category I have hard coded:
from flask import Flask, request
from flask_restful import Resource, Api
app = Flask(__name__)
api = Api(app)
products = []
class Product(Resource):
def post(self, name):
if next(filter(lambda x: x['name'] == name, products), None):
return {'message':
"A product with name '{}' already exists."
.format(name)}, 400
data = request.get_json()
product = {'id': len(products) + 1,
'name': name, 'price': data['price'],
'category': data['category']}
products.append(product)
return product, 201
When I run it I get the JSON data with a 201 OK response.
{
"price": 50,
"category": "stationery",
"name": "pencil",
"id": 2
}
But when I test it:
test_product.py
import unittest
import json
from app import create_app
class ProductTestCase(unittest.TestCase):
"""This is the class for product test cases"""
def setUp(self):
self.app = create_app('testing')
self.client = self.app.test_client
def test_to_add_product(self):
"""Test method to add product"""
"""Is this how the json payload is passed"""
add_product = self.client().post('/api/v1/product/pencil',
data={
"price": 50,
"name": "pencil",
"category": "stationery"
},
content_type='application/json')
self.assertEqual(add_product.status_code, 201)
I get the following error:
E AssertionError: 400 != 201
app/tests/v1/test_product.py:22: AssertionError
What does this mean, and how can I fix it?
EDIT
folowing this answer,How to send requests with JSONs in unit tests, this worked for me:
def test_to_add_product(self):
"""Test method to add product"""
add_product = self.client().post('/api/v1/product/pencil',
data=json.dumps(
dict(category='category',
price='price')),
content_type='application/json')
self.assertEqual(add_product.status_code, 201)

HTTP 400 means "bad request". It indicates that the request you're sending to your API endpoint is somehow invalid.
I don't see where you're passing any JSON data to your endpoint in your test. If JSON data is required (it appears to be since you have data = request.get_json() in your code) that would explain the bad request response.
If you include a valid JSON payload in your test you should see the response you expect.

Related

im trying to make an api in flask that takes in 2 inputs

ive got an api that takes in an id
http://127.0.0.1:5000/api/v1/resources/books?id=u3qR4Ps4TbATrg97
looks like that
what im trying to do after that is add something to the end of the url, for example
http://127.0.0.1:5000/api/v1/resources/books?id=u3qR4Ps4TbATrg97uid=something
im not 100% sure if this is possible
# Create some test data for our catalog in the form of a list of dictionaries.
books = [
{'id': 'u3qR4Ps4TbATrg97',
'uid': 'what',
'title': 'A Fire Upon the Deep',
'author': 'Vernor Vinge',
'first_sentence': 'The coldsleep itself was dreamless.',
'year_published': '1992'}
]
#app.route('/api/v1/resources/books', methods=['GET'])
def api_id():
# Check if an ID was provided as part of the URL.
# If ID is provided, assign it to a variable.
# If no ID is provided, display an error in the browser.
if 'id' and 'uid' in request.args:
id = str(request.args['id'])
uid = str(request.args['uid'])
else:
return "Error: No id field provided. Please specify an id."
results = []
for book in books:
if book['id'] == id:
results.append(book)
if book['uid'] == uid:
results.append(book)
this is what i have so far, mostly copy pasted from here
thats no the whole file just the important bits i can think of
You can add two inputs inside the GET query like this
http://127.0.0.1:5000/api/v1/resources/books?id=u3qR4Ps4TbATrg97&uid=something
Just put an & in between!
Use request.args.get method to get parameters from your url. Also add & to your URL as a parameter separator.
from flask import Flask, request
app = Flask(__name__)
#app.route('/api/v1/resources/books')
def books():
id_ = request.args.get('id')
uid = request.args.get('uid')
return f'id: {id_}, uid: {uid}'
app.run()
Open http://127.0.0.1:5000/api/v1/resources/books?id=u3qR4Ps4TbATrg97&uid=something
in browser and you'll get:
id: u3qR4Ps4TbATrg97, uid: something
Multiple parameters|arguments are passed with & character. ?params1=5&params2=3. For your example: http://127.0.0.1:5000/api/v1/resources/books?id=u3qR4Ps4TbATrg97&uid=what. For the code, I would do:
from flask import Flask, request, jsonify, make_response
app = Flask(__name__)
# Create some test data for our catalog in the form of a list of dictionaries.
books = [
{
"id": "u3qR4Ps4TbATrg97",
"uid": "what",
"title": "A Fire Upon the Deep",
"author": "Vernor Vinge",
"first_sentence": "The coldsleep itself was dreamless.",
"year_published": "1992",
}
]
#app.route("/api/v1/resources/books", methods=["GET"])
def api_id():
# Check if an ID was provided as part of the URL.
# If ID is provided, assign it to a variable.
# If no ID is provided, display an error in the browser.
if set(["id","uid"]).intersection(set(request.args)):
id_ = str(request.args["id"])
uid = str(request.args["uid"])
else:
return make_response(
jsonify({"message": "Error: No id field provided. Please specify an id."}),
400,
)
results = []
for book in books:
if book["id"] == id_:
results.append(book)
if book["uid"] == uid:
results.append(book)
response = make_response(
jsonify({"message": results}),
200,
)
response.headers["Content-Type"] = "application/json"
return response
This would return status code 400 if no match and 200 when match

How can I make my API request working via Postman?

I am working with Postman for the first time and GET request to my simple API is working fine, but when I try POST request it gives me an error and I have no idea where is the problem. Can you please advise?
API function:
#app.route('/customer', methods=['POST'])
def create_customer():
request_data = request.get_json()
new_customer = {
"email": request_data['email'],
"username": request_data['username'],
"name": request_data['name'],
"newsletter_status": request_data['newsletter_status'],
"trips": []
}
for customer in customers:
if customer['username'] == new_customer['username']:
return jsonify({'error': 'username already exist'})
customers.append(new_customer)
return jsonify(new_customer)
Screenshots from postman
This I put in the body + error message
Headers set up - Content-Type application/json
I think your new customer variables should not be in string format, I don't know why they are and also try my option for getting the request body:
import json
def create_customer():
request_data = json.loads(request.body)
new_customer = {
email = request_data['email'],
username = request_data['username'],
name = request_data['name'],
newsletter_status = request_data['newsletter_status'],
trips: []
}
for customer in customers:
if customer['username'] == new_customer['username']:
return jsonify({'error': 'username already exist'})
customers.append(new_customer)
return jsonify(new_customer)
Yur code works fine , i just tested with postman:
from flask import jsonify
from flask import Flask
from flask import request
app = Flask(__name__)
#app.route('/')
def hello_world():
return 'Hello, World!'
#app.route('/customer', methods=['POST'])
def create_customer():
customers=[]
customers.append(request.get_json())
request_data = request.get_json()
new_customer = {
"email": request_data['email'],
"username": request_data['username']+"HHH",
"name": request_data['name'],
"newsletter_status": request_data['newsletter_status'],
"trips": []
}
print(new_customer['username'])
print(customers[0]['username'])
for customer in customers:
if customer['username'] == new_customer['username']:
return jsonify({'error': 'username already exist'})
customers.append(new_customer)
return jsonify(new_customer)
Can you check what is exactly send in postman ?
you can do it by clicking console ( can get from left botom corner or by pressing ctrl+alt+c ) >request > requestbody

Flask preventing PUT, DELETE, and POST

I am trying to test a Flask script I have written, by adding, amending, and deleting information. However, if I run anything other than methods=["GET"], I get an error saying that the method is not allowed.
The script I am running is:
#!flask/bin/python
from flask import Flask, jsonify, request, abort, make_response
from flask_cors import CORS
import csv
stocks = [
{"id": 1, "Stock": "BoI", "Price": 300},
{"id": 2, "Stock": "Apple", "Price": 499}
]
Next_ID = len(stocks)
app = Flask(__name__, static_url_path="", static_folder=".")
CORS(app, support_credentials=True)
#app.route("/")
#app.route("/Stocks")
def getAll():
return jsonify(stocks)
# curl http://127.0.0.1:5000
#app.route("/Stocks/<int:id>")
def getById(id):
sel_stock = list(filter(lambda s: s["id"] == id, stocks))
if len(sel_stock) == 0:
return jsonify ({}), 204
else:
return jsonify(sel_stock[0])
# curl http://127.0.0.1:5000/1
#app.route("/Stocks", methods=["POST"])
def create(id):
global Next_ID
if not request.json:
abort(400)
stock = {
"id": Next_ID,
"Stock": request.json["Stock"],
"Price": request.json["Price"],
}
Next_ID += 1
stocks.append(stock)
return jsonify(stock)
# return str(Next_ID)
#app.route("/Stocks", methods=["PUT"])
def update(id):
sel_stock = list(filter(lambda s: s["id"] == id, stocks))
if len(sel_stock) == 0:
abort(400)
sel_stock = sel_stock[0]
if not request.json:
abort(400)
reqJSON = request.json
if reqJSON["Stock"]:
sel_stock["Stock"] = reqJSON["Stock"]
if reqJSON["Price"]:
sel_stock["Price"] = reqJSON["Price"]
return jsonify(sel_stock)
#app.route("/Stocks", methods=["DELETE"])
def delete(id):
return "in delete"
if __name__ == "__main__":
app.run(debug = True)
If I run curl commands to get all the information or get the information by ID number, there is no issue. However, If I try the following curl command I get an error:
curl -i -H "Content-Type:application/json" -X POST -d "{\"Stock\":\"IBM\",\"Price\":\123}" http://127.0.0.1:5000/Stocks
TypeError: create() missing 1 required positional argument: 'id'
You do not need to pass id to your POST method since its url does not have id as parameter:
#app.route("/Stocks", methods=["POST"])
def create(): # removed id
"""some code here"""

Python keeps KeyError: 'message' when fetch data from Messenger Callback - Python/flask

I've tried to build a simple bot on Messenger that echoing back whenever we send a message to it.
The message is sent successfully so far but in the log, it keeps displaying error:
message_id = data['entry'][0]['messaging'][0]['message']['mid']
KeyError: 'message'
I have no idea why it happen, this is the logic operations:
import requests
import traceback
from flask import request
from ...config.default import VERIFY_TOKEN, ACCESS_TOKEN
ROOT_URL = "https://graph.facebook.com/v2.6/me/messages?access_token="
def reply(user_id, page_id, msg):
data = {
"sender": {"id": page_id},
"recipient": {"id": user_id},
"message": {"text": msg}
}
requests.post(ROOT_URL + ACCESS_TOKEN, json=data)
def verification_handler():
if request.args['hub.verify_token'] == VERIFY_TOKEN:
return request.args['hub.challenge'], 200
return "Invalid verification token"
def handle_incoming_messages(data):
try:
# Get all data from Messenger callback - Text
recipient_id = data['entry'][0]['messaging'][0]['recipient']['id']
sender_id = data['entry'][0]['messaging'][0]['sender']['id']
timestamp = data['entry'][0]['messaging'][0]['timestamp']
message_id = data['entry'][0]['messaging'][0]['message']['mid']
text = data['entry'][0]['messaging'][0]['message']['text']
reply(sender_id, recipient_id, text)
except KeyError:
print(traceback.format_exc())
return ''
this is the routes:
from flask import Blueprint, request
from .ops import verification_handler, handle_incoming_messages
mod = Blueprint('messenger', __name__)
# Route: /messenger/
#mod.route('/', methods=['GET'])
def verify():
"""Facebook will GET request to this endpoint for verification."""
return verification_handler()
# Route: /messenger/
#mod.route('/', methods=['POST'])
def handle():
data = request.json
handle_incoming_messages(data)
return ''
# Route: /messenger/hello
#mod.route('/hello')
def hello():
return 'hello there'
I don't have enough reputation to leave a comment so I have to post this here.
From the Python Wiki
Python raises a KeyError whenever a dict() object is requested (using the format a = adict[key]) and the key is not in the dictionary.
Are you sure the key 'message' exists?
I do this to extract the info
if keys_exist(event, ['body']):
event_entry=json.loads(event['body'])
if ((len(event_entry['entry'])>0) & (keys_exist(event_entry['entry'][0],['messaging'])) ):
messaging_event = event_entry['entry'][0]['messaging'][0]
if (keys_exist(messaging_event,['message'])):
msg_txt = messaging_event['message']['text']
sender_id = messaging_event['sender']['id']

How to create a facebook bot which echoes user input infinite number of times and stops only when the user types a specific command (e.g. bye)?

I have created a fb bot which simply echoes the user input.
I want it to echo the user inputs continuously and stop when the user types in "bye".
How do I do that? Please help out.
Code :
```import os
import sys
import json
import requests
import time
from flask import Flask, request
app = Flask(__name__)
#app.route('/', methods=['GET'])
def verify():
if request.args.get("hub.mode") == "subscribe" and request.args.get("hub.challenge"):
if not request.args.get("hub.verify_token") == os.environ["VERIFY_TOKEN"]:
return "Verification token mismatch", 403
return request.args["hub.challenge"], 200
return "Hello world", 200
#app.route('/', methods=['POST'])
def webhook():
data = request.get_json()
if data["object"] == "page":
for entry in data["entry"]:
for messaging_event in entry["messaging"]:
if messaging_event.get("message"): # someone sent us a message
sender_id = messaging_event["sender"]["id"]
recipient_id = messaging_event["recipient"]["id"]
message_text = messaging_event["message"]["text"]
reply = "Received : " + message_text
if "bye" in message_text.lower():
reply = "Good-bye"
send_message(sender_id, reply)
return "ok", 200
def send_message(recipient_id, message_text):
log("sending message to {recipient}: {text}".format(recipient=recipient_id, text=message_text))
params = {
"access_token": os.environ["PAGE_ACCESS_TOKEN"]
}
headers = {
"Content-Type": "application/json"
}
data = json.dumps({
"recipient": {
"id": recipient_id
},
"message": {
"text": message_text
}
})
r = requests.post("https://graph.facebook.com/v2.6/me/messages", params=params, headers=headers, data=data)
if r.status_code != 200:
log(r.status_code)
log(r.text)
if __name__ == '__main__':
app.run(debug=True) ```
How about return without send_message(sender_id, reply) function? Like this
if "bye" in message_text.lower():
return "bye", 200
This code is very similar to yours, except the fact that I used class based view
To break the loop add fbid of the user in a file or a DB after he/she says bye.
Everytime a message is posted check fbid to see whether user had already said "bye". If yes return empty response
def post_facebook_message(fbid, recevied_message):
"""
takes a user fb id and posts the bot message as response
"""
post_message_url = 'https://graph.facebook.com/v2.6/me/messages?access_token='+PAGE_ACCESS_TOKEN
response_msg = json.dumps({"recipient":{"id":fbid}, "message":{"text":recevied_message}})
status = requests.post(post_message_url, headers={"Content-Type": "application/json"},data=response_msg)
print status.json()
class FBBotView(generic.View):
#this is to verify your FB account with your program
#use ythe same verify token used while registering the bot
#method_decorator(csrf_exempt)
def get(self, request, *args, **kwargs):
if self.request.GET['hub.verify_token'] == VERIFY_TOKEN:
return HttpResponse(self.request.GET['hub.challenge'])
else:
return HttpResponse('Error, invalid token')
#method_decorator(csrf_exempt)
def dispatch(self, request, *args, **kwargs):
return generic.View.dispatch(self, request, *args, **kwargs)
# Post function to handle Facebook messages
def post(self, request, *args, **kwargs):
# Converts the text payload into a python dictionary
incoming_message = json.loads(self.request.body.decode('utf-8'))
# Facebook recommends going through every entry since they might send
# multiple messages in a single call during high load
for entry in incoming_message['entry']:
for message in entry['messaging']:
# Check to make sure the received call is a message call
# This might be delivery, optin, postback for other events
if 'message' in message:
if recieved_message.lower() == "bye":
#sends an empty response
message = "Good bye"
post_facebook_message(message['sender']['id'],message)
#this line is important because many times FB checks validity of webhook.
return HttpResponse()

Categories

Resources