You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用pytest向Flask表单传递数据完成登录功能测试?

根本原因

你的Flask-WTF表单默认开启了CSRF(跨站请求伪造)保护,你在测试代码中直接发送POST请求时没有携带CSRF令牌,导致表单验证直接失败,所以永远进不了validate_on_submit()的分支逻辑。
还有一个次要的小问题:WTForms的BooleanField在表单提交场景下,未勾选状态是不会传递参数的,你直接传布尔值False会被序列化为字符串'False',反而会被识别为勾选状态。

解决方案

下面提供两种常用的解决方式,可根据你的测试场景选择:

方案1:测试环境禁用CSRF保护(单元测试首选,配置简单)

直接在测试的app配置中关闭CSRF校验即可,不需要修改其他业务逻辑。
修改你的conftest.py中的app fixture代码:

@pytest.fixture(scope='module')
def app():
    app = init_app()
    # 测试环境关闭CSRF保护
    app.config['WTF_CSRF_ENABLED'] = False
    with app.app_context():
        db.create_all()
        user = User(username='testuser', email='test@gmail.com', forename='Test', surname='User', confirmed=True)
        user.set_password('testing')
        db.session.add(user)
        db.session.commit()
        yield app

同时调整测试用例中的formdata,去掉remember_me参数(不传递就代表未勾选):

def test_index_page__logged_in(client):
    with client:
        formdata = {'username': 'testuser', 'password': 'testing'}
        client.post('/auth/login', data=formdata)
        assert current_user.username == 'testuser'

方案2:保留CSRF保护(集成测试首选,贴近真实使用场景)

如果需要测试完整的表单流程包括CSRF校验,可以先发送GET请求获取登录页面的CSRF令牌,再携带令牌发送POST请求。
修改后的测试用例代码如下:

def test_index_page__logged_in(client):
    with client:
        # 先发起GET请求获取csrf令牌
        response = client.get('/auth/login')
        # 从响应的cookie中提取csrf_token,Flask-WTF默认会把token放在X-CSRFToken的cookie里
        csrf_token = response.headers.get('Set-Cookie').split('X-CSRFToken=')[1].split(';')[0]
        # 构造带csrf_token的表单数据
        formdata = {
            'username': 'testuser', 
            'password': 'testing',
            'csrf_token': csrf_token
        }
        client.post('/auth/login', data=formdata)
        assert current_user.username == 'testuser'

两种方案都可以解决表单验证不通过的问题,单元测试场景选方案1即可,需要模拟真实用户操作的集成测试场景推荐选方案2。


内容的提问来源于stack exchange,提问作者MartynW

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.13 07:54:02