单元测试包含多个提交按钮的Flask表单

时间:2016-02-17 12:29:30

标签: unit-testing flask pytest werkzeug

我正在为Flask应用程序中的表单验证方法编写单元测试,该应用程序包含几个不同的Submit按钮来控制逻辑流程。

表单验证方法希望收到一个ImmutibleMultiDict对象,其中包含按钮名称和值('btn', 'Save')('btn', 'Update')('btn', 'Delete')。不幸的是,我无法弄清楚如何在pytest中模拟或提供不同的按钮响应。

以下是表单验证方法中的示例代码,其中包含一些不同的操作,具体取决于提交时使用的按钮(更新'或者'保存'):

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'或{{ 1}}。

request.form['btn'] = 'Update'

下面是测试失败的错误代码及其出错的原因是因为验证期望werkzeug ImmutibleMutltiDict对象包含用于提交表单的按钮的名称,但我没有正确提供ImmutibleMultiDict对象中的按钮名称。

我已经尝试了很多东西,但在下面的测试中注释了一个例子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 ,它无法正常工作,因为无法直接修改ImmutibleMutliDict对象:

request.form.add('btn','Save')

要正确测试表单验证,ImmutableMultiDict对象应该如下所示,包括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 数据:

('btn', 'Save')

我正在使用pytest和factory-boy,下面是相关的pytest夹具和工厂。我尝试过创建其他包含按钮数据的pytest灯具,但这对我来说也没用:

This is reqest.form =>ImmutableMultiDict([('cn_name', '中地'), ('btn', 'Save'), 
('en_name', 'Middle Earth'), 
('csrf_token', '1455956207##90932fcb2d1481be007f90e32040b6aba3e5fe68')])

我认为按钮应存储在@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 之类的字典中,并且可供测试框架访问,但我无法找到最佳实现方式。谢谢!

1 个答案:

答案 0 :(得分:2)

如果您正在尝试测试刻录逻辑(包括表单行为),Flask已经内置了这样做的方法,您可以注入自己的POST,GET值:http://flask.pocoo.org/docs/0.10/testing/

但您似乎正在尝试做的是专门测试表单的验证逻辑。在这种情况下,您要做的是修改请求上下文并将您的按钮值注入request.form(基本上用您自己的替换ImmutableMultiDict())。这必须在请求上下文中完成。请参阅上面的链接。

下面是一些示例代码,展示了如何实现这一目标:

表格

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功能应打印&#39;正在保存...&#39;然后更新!&#39;。当然,您需要将其余的相关数据添加到ImmutableMultiDict。