测试 Flask 应用程序时保留多个功能的更改
Preserve changes in multiple function when testing a Flask app
我正在关注 Flask 上关于创建 API 的演讲。我想为它写一些测试。在测试创建资源时,测试删除另一个功能中的资源是否有效?如何使资源的创建持久化以进行删除和编辑测试?
David Baumgold - Prototyping New APIs with Flask - PyCon 2016
演讲展示了如何为小狗的名字和图片网址制作 API。
- 您在索引页上创建了一只小狗 POST 请求
- 你从'/puppy_name'
获得了一只小狗
- 你从'/'
获得了带有 GET 的小狗列表
- 您使用来自“/puppy_name”的 PUT 编辑了一只小狗(当然还有新数据)
- 您从“/puppy_name”
中删除了带有 DELETE 的小狗
import py.test
import unittest
from requests import get, post, delete, put
localhost = 'http://localhost:5000'
class TestApi(unittest.TestCase):
def test_list_puppies(self):
index = get(localhost)
assert index.status_code == 200
def test_get_puppy(self):
puppy1 = get(localhost + '/rover')
puppy2 = get(localhost + '/spot')
assert puppy1.status_code == 200 and puppy2.status_code == 200
def test_create_puppy(self):
create = post(localhost, data={
'name': 'lassie', 'image_url': 'lassie_url'})
assert create.status_code == 201
@py.test.mark.skip('cannot fix it')
def test_edit_puppy(self):
ret = put(localhost + '/lassie',
data={'name': 'xxx', 'image_url': 'yyy'})
assert ret.status_code == 201
def test_puppy_exits(self):
lassie = get(localhost + '/lassie').status_code
assert lassie == 200
def test_delete_puppy(self):
ret = delete(localhost + '/lassie')
assert ret.status_code == 200
@app.route('/', methods=['POST'])
def create_puppy():
puppy, errors = puppy_schema.load(request.form)
if errors:
response = jsonify(errors)
response.status_code = 400
return response
puppy.slug = slugify(puppy.name)
# create in database
db.session.add(puppy)
db.session.commit()
# return an HTTP response
response = jsonify( {'message': 'puppy created'} )
response.status_code = 201
location = url_for('get_puppy', slug=puppy.slug)
response.headers['location'] = location
return response
@app.route('/<slug>', methods=['DELETE'])
def delete_puppy(slug):
puppy = Puppy.query.filter(Puppy.slug == slug).first_or_404()
db.session.delete(puppy)
db.session.commit()
return jsonify( {'message': '{} deleted'.format(puppy.name)} )
'test_edit_puppy' 和 'test_puppy_exists' 中的断言语句均失败。我得到一个 404 状态代码,而不是 201 和 200。
你测试错了。当您 运行 进行测试时,您 可以 将更改持久保存到数据库,只需提交它即可,但您真的不想那样做。
通过单元测试,您可以测试简单的单元。对于集成测试,这就是您在这里所说的,您仍然希望每个测试都有一个特定的重点。在这种特殊情况下,您可能希望执行以下操作:
def test_delete_puppy(self):
create = post(localhost, data={
'name': 'lassie', 'image_url': 'lassie_url'})
lassie = get(localhost + '/lassie')
# not sure if there's an "assume" method, but I would
# use that here - the test is not a valid test
# if you can't create a puppy and retrieve the puppy
# then there's really no way to delete something that
# does not exist
assert create.status_code == 201
assert lassie.status_code == 200
ret = delete(localhost + '/lassie')
assert ret.status_code == 200
lassie = get(localhost + '/lassie')
assert lassie.status_code == 404
本次测试的重点是测试删除puppy是否正常。但是您希望将数据库中的小狗设置为测试的一部分或测试设置的一部分。它是 arrange, act, assert
的 arrange
部分。在实际执行测试之前,您要按正确的顺序安排世界的状态。集成测试和单元测试之间的区别在于,通过单元测试,您将模拟出您正在调用的所有端点,而通过集成测试,您将在 [=23] 之前实际设置您需要的所有数据=] 测试部分。这就是您想在此处执行的操作。
我正在关注 Flask 上关于创建 API 的演讲。我想为它写一些测试。在测试创建资源时,测试删除另一个功能中的资源是否有效?如何使资源的创建持久化以进行删除和编辑测试?
David Baumgold - Prototyping New APIs with Flask - PyCon 2016
演讲展示了如何为小狗的名字和图片网址制作 API。
- 您在索引页上创建了一只小狗 POST 请求
- 你从'/puppy_name' 获得了一只小狗
- 你从'/' 获得了带有 GET 的小狗列表
- 您使用来自“/puppy_name”的 PUT 编辑了一只小狗(当然还有新数据)
- 您从“/puppy_name” 中删除了带有 DELETE 的小狗
import py.test
import unittest
from requests import get, post, delete, put
localhost = 'http://localhost:5000'
class TestApi(unittest.TestCase):
def test_list_puppies(self):
index = get(localhost)
assert index.status_code == 200
def test_get_puppy(self):
puppy1 = get(localhost + '/rover')
puppy2 = get(localhost + '/spot')
assert puppy1.status_code == 200 and puppy2.status_code == 200
def test_create_puppy(self):
create = post(localhost, data={
'name': 'lassie', 'image_url': 'lassie_url'})
assert create.status_code == 201
@py.test.mark.skip('cannot fix it')
def test_edit_puppy(self):
ret = put(localhost + '/lassie',
data={'name': 'xxx', 'image_url': 'yyy'})
assert ret.status_code == 201
def test_puppy_exits(self):
lassie = get(localhost + '/lassie').status_code
assert lassie == 200
def test_delete_puppy(self):
ret = delete(localhost + '/lassie')
assert ret.status_code == 200
@app.route('/', methods=['POST'])
def create_puppy():
puppy, errors = puppy_schema.load(request.form)
if errors:
response = jsonify(errors)
response.status_code = 400
return response
puppy.slug = slugify(puppy.name)
# create in database
db.session.add(puppy)
db.session.commit()
# return an HTTP response
response = jsonify( {'message': 'puppy created'} )
response.status_code = 201
location = url_for('get_puppy', slug=puppy.slug)
response.headers['location'] = location
return response
@app.route('/<slug>', methods=['DELETE'])
def delete_puppy(slug):
puppy = Puppy.query.filter(Puppy.slug == slug).first_or_404()
db.session.delete(puppy)
db.session.commit()
return jsonify( {'message': '{} deleted'.format(puppy.name)} )
'test_edit_puppy' 和 'test_puppy_exists' 中的断言语句均失败。我得到一个 404 状态代码,而不是 201 和 200。
你测试错了。当您 运行 进行测试时,您 可以 将更改持久保存到数据库,只需提交它即可,但您真的不想那样做。
通过单元测试,您可以测试简单的单元。对于集成测试,这就是您在这里所说的,您仍然希望每个测试都有一个特定的重点。在这种特殊情况下,您可能希望执行以下操作:
def test_delete_puppy(self):
create = post(localhost, data={
'name': 'lassie', 'image_url': 'lassie_url'})
lassie = get(localhost + '/lassie')
# not sure if there's an "assume" method, but I would
# use that here - the test is not a valid test
# if you can't create a puppy and retrieve the puppy
# then there's really no way to delete something that
# does not exist
assert create.status_code == 201
assert lassie.status_code == 200
ret = delete(localhost + '/lassie')
assert ret.status_code == 200
lassie = get(localhost + '/lassie')
assert lassie.status_code == 404
本次测试的重点是测试删除puppy是否正常。但是您希望将数据库中的小狗设置为测试的一部分或测试设置的一部分。它是 arrange, act, assert
的 arrange
部分。在实际执行测试之前,您要按正确的顺序安排世界的状态。集成测试和单元测试之间的区别在于,通过单元测试,您将模拟出您正在调用的所有端点,而通过集成测试,您将在 [=23] 之前实际设置您需要的所有数据=] 测试部分。这就是您想在此处执行的操作。