Unit testing 单元测试包含多个提交按钮的烧瓶表单

Unit testing 单元测试包含多个提交按钮的烧瓶表单,unit-testing,flask,pytest,werkzeug,Unit Testing,Flask,Pytest,Werkzeug,我正在为Flask应用程序中的表单验证方法编写单元测试,该应用程序包含几个不同的提交按钮来控制逻辑流 表单验证方法希望接收一个ImmutibleMultiDict对象,该对象包括按钮名称和值,如('btn','Save')或('btn','Update')或('btn','Delete')。不幸的是,我不知道如何在pytest中模拟或提供不同的按钮响应 下面是表单验证方法的示例代码,其中包含一些不同的操作,具体取决于提交中使用的按钮(“更新”或“保存”): 表单验证方法的以下测试不起作用,因为缺

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

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

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

表单验证方法的以下测试不起作用,因为缺少与测试中的表单验证逻辑相匹配的按钮名称值信息,该逻辑期望检查是否存在
请求。表单['btn']='Save'
请求。表单['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 ImmutibleMultiDict对象,该对象包含用于提交表单的按钮名称,但我没有在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
我使用的是pytest和factory boy,下面是相关的pytest夹具和factory。我曾尝试创建其他包含按钮数据的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,获取值:

但是看起来您要做的是专门测试表单的验证逻辑。在这种情况下,您需要做的是修改请求上下文并将您的按钮值注入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\u sample\u form\u validate
函数应打印“Saving…”,然后打印“update!”。当然,您需要将其余相关数据添加到ImmutableMultiDict中。

在上面的赏金中,我指的是
request.form['btn']='Save'
request.form['btn']='Update'
这是request.form=>ImmutableMultiDict([('cn\u name','中地'), ('btn','Save'),('en_name','Middle Earth'),('csrf#u token','1455956207###90932fcb2d1481be007f90e32040b6aba3e5fe68'))如果您还没有看到这一点:我不确定我是否完全理解您正在尝试执行的操作。您需要知道在您的测试\u验证\u大陆\u cn\u名称\u已注册的函数中按下了哪个按钮?或者您正在尝试为按钮分配一个值,以便模拟“保存”按钮单击或“更新”按钮单击?这会有所帮助如果你(在代码中)概述了你要做的事情,我会感到很遗憾。@junnythony谢谢你的反馈aI已经更新了问题以澄清。我需要模拟“保存”或“更新”或“删除”按钮的点击。表单验证需要一个不可变的multidict对象,包括例如
('btn','Save)
在“保存”的情况下添加
request.form=ImmutableMultiDict([('btn','Save')])
解决了我的问题。非常感谢。
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!'