对包含多个提交按钮的 Flask 表单进行单元测试
Unit testing a Flask form containing multiple submit buttons
我正在为 Flask 应用程序中的表单验证方法编写单元测试,该应用程序包含几个不同的提交按钮来控制逻辑流。
表单验证方法需要接收一个 ImmutibleMultiDict
对象,其中包含按钮名称和值,例如 ('btn', 'Save')
或 ('btn', 'Update')
或 ('btn', 'Delete')
。不幸的是,我不知道如何在 pytest 中模拟或提供不同的按钮响应。
下面是表单验证方法的示例代码,根据提交时使用的按钮('Update' 或 'Save')具有一些不同的操作:
def validate(self):
if request.form['btn'] == 'Update':
if cn_continent_name and en_continent_name:
flash('You have not made a change. There is nothing to update.', 'warning')
return False
if not _check_clean_chinese():
return False
if request.form['btn'] == 'Save':
# check if Chinese name already exists in the DB
if cn_continent_name:
self.cn_name.errors.append("Chinese Continent Name already registered")
return False
# check the if English name already exists in the DB
en_continent_name = ContinentsTable.query.filter_by(en_name=self.en_name.data).first()
if en_continent_name:
self.en_name.errors.append("English Country Name already registered")
return False
下面的表单验证方法测试不起作用,因为缺少按钮名称-值信息以匹配被测表单验证逻辑,该逻辑期望检查是否存在 request.form['btn'] = 'Save'
或request.form['btn'] = 'Update'
。
class TestContinentsForm:
"""Continents form."""
def test_validate_continent_cn_name_already_registered(self, continent):
"""Enter Continent cn_name that is already registered."""
form = ContinentsForm(cn_name=continent.cn_name, en_name='NewEngName')
assert form.validate() is False
assert 'Chinese Continent Name already registered' in form.cn_name.errors
下面是测试失败的错误代码,它出错的原因是因为验证需要一个 werkzeug ImmutibleMutltiDict 对象,其中包含用于提交表单的按钮的名称,但我没有正确在 ImmutibleMultiDict 对象中提供按钮名称。
我已经尝试了很多东西,但在下面的测试中注释掉了一个例子 request.form.add('btn','Save')
它不起作用,因为不能直接修改 ImmutibleMutliDict 对象:
self = <tests.test_forms.TestContinentsForm object at 0x10f8be908>
continent = Asia, 亚洲, yà zhōu!
def test_validate_continent_cn_name_already_registered(self, continent):
"""Enter Continent cn_name that is already registered."""
form = ContinentsForm(cn_name=continent.cn_name, en_name='NewEngName')
#request.form.add('btn','Save')
#assert 'Save' in request.form
>assert form.validate() is False
test_forms.py:96:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
../aoscrdb_app/user/forms/locations/continents_form.py:70: in validate
if 'Delete' in request.form['btn']:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = ImmutableMultiDict([]), key = 'btn'
def __getitem__(self, key):
"""Return the first data value for this key;
raises KeyError if not found.
:param key: The key to be looked up.
:raise KeyError: if the key does not exist.
"""
if key in self:
return dict.__getitem__(self, key)[0]
>raise exceptions.BadRequestKeyError(key)
E werkzeug.exceptions.HTTPException.wrap.<locals>.newcls: 400: Bad Request
../venv/lib/python3.5/site-packages/werkzeug/datastructures.py:402: BadRequestKeyError
为了正确测试表单验证,ImmutableMultiDict 对象应该如下所示,包括 ('btn', 'Save')
数据:
This is reqest.form =>ImmutableMultiDict([('cn_name', '中地'), ('btn', 'Save'),
('en_name', 'Middle Earth'),
('csrf_token', '1455956207##90932fcb2d1481be007f90e32040b6aba3e5fe68')])
我正在使用 pytest 和 factory-boy,下面是相关的 pytest 夹具和工厂。我试过创建其他包含按钮数据的 pytest 装置,但这对我也不起作用:
@pytest.fixture()
def continent(db):
"""A continent for the tests."""
continent = ContinentFactory()
db.session.commit()
return continent
class ContinentFactory(BaseFactory):
"""Continent factory."""
cn_name = '亚洲'
en_name = 'Asia'
class Meta:
"""Factory configuration."""
model = ContinentsTable
我认为按钮应该存储在像 {'btn': 'Save'}
这样的字典中,并且测试框架可以访问这些按钮,但我找不到最佳的实现方式。谢谢!
如果您正在尝试测试您的 Flask 逻辑(包括表单行为),Flask 已经内置了这样做的方法,您可以注入自己的 POST,GET 值:http://flask.pocoo.org/docs/0.10/testing/
但您似乎想做的是专门测试表单的验证逻辑。在这种情况下,您要做的是修改请求上下文并将您的按钮值注入 request.form (基本上用您自己的替换 ImmutableMultiDict() )。这必须在请求上下文中完成。参见上面的link。
下面是一些示例代码,展示了如何实现这一点:
表格
import wtforms
class SampleForm(wtforms.Form):
btn = wtforms.fields.SubmitField('Cancel')
def validate(self):
if request.form['btn'] == 'Save':
print('Saving...')
elif request.form['btn'] == 'Update':
print('Updating!')
else:
print('Some other btn action')
测试
from flask import Flask, request
from werkzeug import ImmutableMultiDict
def test_sample_form_validate():
app = Flask(__name__)
form = SampleForm()
with app.test_request_context('/'):
request.form = ImmutableMultiDict([('btn', 'Save')])
form.validate() # Prints 'Saving...'
request.form = ImmutableMultiDict([('btn', 'Update')])
form.validate() # Prints 'Updating!'
运行 test_sample_form_validate
函数应该打印 'Saving...' 后跟 'Updating!'。当然,您需要将其余相关数据添加到 ImmutableMultiDict。
我正在为 Flask 应用程序中的表单验证方法编写单元测试,该应用程序包含几个不同的提交按钮来控制逻辑流。
表单验证方法需要接收一个 ImmutibleMultiDict
对象,其中包含按钮名称和值,例如 ('btn', 'Save')
或 ('btn', 'Update')
或 ('btn', 'Delete')
。不幸的是,我不知道如何在 pytest 中模拟或提供不同的按钮响应。
下面是表单验证方法的示例代码,根据提交时使用的按钮('Update' 或 'Save')具有一些不同的操作:
def validate(self):
if request.form['btn'] == 'Update':
if cn_continent_name and en_continent_name:
flash('You have not made a change. There is nothing to update.', 'warning')
return False
if not _check_clean_chinese():
return False
if request.form['btn'] == 'Save':
# check if Chinese name already exists in the DB
if cn_continent_name:
self.cn_name.errors.append("Chinese Continent Name already registered")
return False
# check the if English name already exists in the DB
en_continent_name = ContinentsTable.query.filter_by(en_name=self.en_name.data).first()
if en_continent_name:
self.en_name.errors.append("English Country Name already registered")
return False
下面的表单验证方法测试不起作用,因为缺少按钮名称-值信息以匹配被测表单验证逻辑,该逻辑期望检查是否存在 request.form['btn'] = 'Save'
或request.form['btn'] = 'Update'
。
class TestContinentsForm:
"""Continents form."""
def test_validate_continent_cn_name_already_registered(self, continent):
"""Enter Continent cn_name that is already registered."""
form = ContinentsForm(cn_name=continent.cn_name, en_name='NewEngName')
assert form.validate() is False
assert 'Chinese Continent Name already registered' in form.cn_name.errors
下面是测试失败的错误代码,它出错的原因是因为验证需要一个 werkzeug ImmutibleMutltiDict 对象,其中包含用于提交表单的按钮的名称,但我没有正确在 ImmutibleMultiDict 对象中提供按钮名称。
我已经尝试了很多东西,但在下面的测试中注释掉了一个例子 request.form.add('btn','Save')
它不起作用,因为不能直接修改 ImmutibleMutliDict 对象:
self = <tests.test_forms.TestContinentsForm object at 0x10f8be908>
continent = Asia, 亚洲, yà zhōu!
def test_validate_continent_cn_name_already_registered(self, continent):
"""Enter Continent cn_name that is already registered."""
form = ContinentsForm(cn_name=continent.cn_name, en_name='NewEngName')
#request.form.add('btn','Save')
#assert 'Save' in request.form
>assert form.validate() is False
test_forms.py:96:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
../aoscrdb_app/user/forms/locations/continents_form.py:70: in validate
if 'Delete' in request.form['btn']:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = ImmutableMultiDict([]), key = 'btn'
def __getitem__(self, key):
"""Return the first data value for this key;
raises KeyError if not found.
:param key: The key to be looked up.
:raise KeyError: if the key does not exist.
"""
if key in self:
return dict.__getitem__(self, key)[0]
>raise exceptions.BadRequestKeyError(key)
E werkzeug.exceptions.HTTPException.wrap.<locals>.newcls: 400: Bad Request
../venv/lib/python3.5/site-packages/werkzeug/datastructures.py:402: BadRequestKeyError
为了正确测试表单验证,ImmutableMultiDict 对象应该如下所示,包括 ('btn', 'Save')
数据:
This is reqest.form =>ImmutableMultiDict([('cn_name', '中地'), ('btn', 'Save'),
('en_name', 'Middle Earth'),
('csrf_token', '1455956207##90932fcb2d1481be007f90e32040b6aba3e5fe68')])
我正在使用 pytest 和 factory-boy,下面是相关的 pytest 夹具和工厂。我试过创建其他包含按钮数据的 pytest 装置,但这对我也不起作用:
@pytest.fixture()
def continent(db):
"""A continent for the tests."""
continent = ContinentFactory()
db.session.commit()
return continent
class ContinentFactory(BaseFactory):
"""Continent factory."""
cn_name = '亚洲'
en_name = 'Asia'
class Meta:
"""Factory configuration."""
model = ContinentsTable
我认为按钮应该存储在像 {'btn': 'Save'}
这样的字典中,并且测试框架可以访问这些按钮,但我找不到最佳的实现方式。谢谢!
如果您正在尝试测试您的 Flask 逻辑(包括表单行为),Flask 已经内置了这样做的方法,您可以注入自己的 POST,GET 值:http://flask.pocoo.org/docs/0.10/testing/
但您似乎想做的是专门测试表单的验证逻辑。在这种情况下,您要做的是修改请求上下文并将您的按钮值注入 request.form (基本上用您自己的替换 ImmutableMultiDict() )。这必须在请求上下文中完成。参见上面的link。
下面是一些示例代码,展示了如何实现这一点:
表格
import wtforms
class SampleForm(wtforms.Form):
btn = wtforms.fields.SubmitField('Cancel')
def validate(self):
if request.form['btn'] == 'Save':
print('Saving...')
elif request.form['btn'] == 'Update':
print('Updating!')
else:
print('Some other btn action')
测试
from flask import Flask, request
from werkzeug import ImmutableMultiDict
def test_sample_form_validate():
app = Flask(__name__)
form = SampleForm()
with app.test_request_context('/'):
request.form = ImmutableMultiDict([('btn', 'Save')])
form.validate() # Prints 'Saving...'
request.form = ImmutableMultiDict([('btn', 'Update')])
form.validate() # Prints 'Updating!'
运行 test_sample_form_validate
函数应该打印 'Saving...' 后跟 'Updating!'。当然,您需要将其余相关数据添加到 ImmutableMultiDict。