問題描述
將 JSON 文件作為 Post Request Flask 發送 (Sending JSON File as Post Request Flask)
我正在編寫一個測試來從社區中檢索前 n 個帖子。
在獲取這些值之前,我想從 JSON 文件中發布數據,而不是將它們分別發佈到端點。
我嘗試了 test_retrieve_recent_posts_existing_community
的細微變化,但不斷收到 TypeError
。
誰能建議我如何修復測試用例?
Route:
@app.route('/api/v2/post/retrieve/<community>/<top>', methods=['GET'])
def recent_community_posts(community=None, top=0):
community = request.args.get('community')
top = request.args.get('top')
filtered_posts = []
if community:
for post in posts:
if post['community'] == community:
filtered_posts.append(post)
if len(filtered_posts) == 0:
return {'message': 'Community does not exist ...', 'status': 402}
return {'data': jsonify(filtered_posts[:top]), 'message': "Filtered data based on community", 'status': 203}
Test:
def test_retrieve_recent_posts_existing_community(client):
with open('posts.json') as posts_data:
posts_json = json.dumps(posts_data)
client.post('/api/v2/post/create', data=json.dump(posts_json), content_type='application/json')
response = client.get('/api/v2/post/retrieve/tech/2')
data = json.load(response.get_data(as_text=True))
assert "Filtered data" in data["message"] and data["status"] == 203
Error:
TypeError:TextIOWrapper 類型的對像不是 JSON 可序列化的
文件(posts.json):
[
{
"postid": 2,
"title": "Heading 2",
"text": "Content of post 2",
"published": "YYYY/MM/DD hh:mm:ss",
"community": "tech"
},
{
"postid": 3,
"title": "Heading 3",
"text": "Content of post 3",
"published": "YYYY/MM/DD hh:mm:ss",
"community": "tech"
},
{
"postid": 4,
"title": "Heading 4",
"text": "Content of post 4",
"published": "YYYY/MM/DD hh:mm:ss",
"community": "art"
},
{
"postid": 5,
"title": "Heading 5",
"text": "Content of post 5",
"published": "YYYY/MM/DD hh:mm:ss",
"community": "art"
},
{
"postid": 6,
"title": "Heading 6",
"text": "Content of post 6",
"published": "YYYY/MM/DD hh:mm:ss",
"community": "science"
},
{
"postid": 7,
"title": "Heading 7",
"text": "Content of post 7",
"published": "YYYY/MM/DD hh:mm:ss",
"community": "tech"
}
]
參考解法
方法 1:
All errors in your test are caused by wrong usage of json module methods.
Here is a modified version of your test with comments on the changes
def test_retrieve_recent_posts_existing_community(client):
with open('posts.json') as posts_data:
posts_json = json.load(posts_data) # posts data is a TextIOWrapper object. you read it using load not dumps
# posts_json is a list and should be turned into a json string using json.dumps not dump
client.post('/api/v2/post/create', data=json.dumps(posts_json), content_type='application/json')
response = client.get('/api/v2/post/retrieve/tech/2')
data = json.loads(response.get_data(as_text=True)) # use loads not load to read the json string.
assert "Filtered data" in data["message"] and data["status"] == 203
Notice that you also have a problem in the create method. It expects one object to be passed in the json body. and you're sending an array. So either change your json file to contain one object or change the create implementation to deal with an array of objects. for example change it to the following
def create():
data_list = request.get_json()
for data in data_list:
postid = data['postid']
title = data['title']
text = data['text']
published = data['published']
community = data['community']
new_post = {
'postid': postid,
'title': title,
'text': text,
'published': published,
'community': community
}
posts.append(new_post)
return {'message': "Post created successfully!", 'status': 202}
At this point the test should run, but it would result in AssertionError. That is because you have two problem in the implementation of recent_community_posts() method.
You're extracting query parameters in the first two lines and you pass the "community" and the "top" as path parameters. so
if community
will always return false unless you pass it as a parameter in the url (/api/v2/post/retrieve/tech/2?community=tech&top=2) which is redundant.You're using jsonify inside the dictionary in the return statement like this:
return {'data': jsonify(filtered_posts[:top]), 'message': "Filtered data based on community", 'status': 203}
jsonify returns a flask.wrappers.Response object which is not JSON serializable. you can replace it with the following
return jsonify({
'data': filtered_posts[:top],
'message': "Filtered data based on community",
'status': 203
})
方法 2:
To upload the body of the request:
with open('posts.json') as posts_data:
posts_json = json.load(posts_data)
Which output should be a list:
print(type(posts_json))
list
As to post the body should by dumped:
data=json.dumps(posts_json)
Which is a string:
Out[9]: '[{"postid": 2, "title": "Heading 2", "text": "Content of post 2", "published": "YYYY/MM/DD hh:mm:ss", "community": "tech"}, {"postid": 3, "title": "Heading 3", "text": "Content of post 3", "published": "YYYY/MM/DD hh:mm:ss", "community": "tech"}, {"postid": 4, "title": "Heading 4", "text": "Content of post 4", "published": "YYYY/MM/DD hh:mm:ss", "community": "art"}, {"postid": 5, "title": "Heading 5", "text": "Content of post 5", "published": "YYYY/MM/DD hh:mm:ss", "community": "art"}, {"postid": 6, "title": "Heading 6", "text": "Content of post 6", "published": "YYYY/MM/DD hh:mm:ss", "community": "science"}, {"postid": 7, "title": "Heading 7", "text": "Content of post 7", "published": "YYYY/MM/DD hh:mm:ss", "community": "tech"}]'
I think you have posts_json dumped twice. Could you try with only once?. Please.
def test_retrieve_recent_posts_existing_community(client):
with open('posts.json') as posts_data:
posts_json = json.load(posts_data)
#SMALL CHANGE BELOW
client.post('/api/v2/post/create', data=json.dumps(posts_json), content_type='application/json')
response = client.get('/api/v2/post/retrieve/tech/2')
data = json.load(response.get_data(as_text=True))
assert "Filtered data" in data["message"] and data["status"] == 203
方法 3:
In your test_retrieve_recent_posts_existing_community function, you're doing
posts_json = json.dumps(posts_data)
Now json.dumps takes a python object (dicts and lists) and turns it into a string, which is the textual representation in JSON. But that is already what you have in your file. Just do this instead:
with open('posts.json') as posts_data:
posts_json = posts_data.read()
and again, in the client.post call, just do
data=posts_json
no need for json.dump.
(by Saurabh、Mostafa Kashwaa、Rafael Valero、joao)