如何使用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
相关产品推荐
相关产品推荐

