如何测试Django PasswordChangeForm?表单验证通过但视图提交报错
我正尝试为使用PasswordChangeForm的密码修改视图编写测试,测试代码如下:
def test_profile_change_password(self): self.client.force_login(self.user) self.user.set_password(self.password) new_password = random_password() payload = { "old_password": self.password, "new_password1": new_password, "new_password2": new_password, } form = PasswordChangeForm(user=self.user, data=payload) self.assertTrue(form.is_valid()) response = self.client.get( reverse_lazy("researcher_ui:change_password"), ) self.assertEqual(response.template_name, ["researcher_UI/change_password.html"]) response = self.client.post( reverse_lazy("researcher_ui:change_password"), payload ) print(response.context['form'].error_messages)
单独验证表单时self.assertTrue(form.is_valid())测试通过,但使用client.post提交相同数据到视图时,却收到错误信息:{'password_mismatch': 'The two password fields didn’t match.', 'password_incorrect': 'Your old password was entered incorrectly. Please enter it again.'}。按逻辑表单验证通过就不应出现这类错误,请问我哪里操作有误?
分析与解决方法
1. 核心问题:修改密码后未保存用户实例
你调用self.user.set_password(self.password)仅在内存中修改了用户的密码哈希值,但没有将该变更保存到数据库。单独验证表单时,使用的是内存中的用户对象,验证能正常通过;但client.post请求视图时,视图会从数据库读取用户数据,此时数据库里的用户密码仍是初始值,因此触发「旧密码错误」的验证失败。
修正方式:在set_password后添加保存操作:
self.user.set_password(self.password) self.user.save() # 新增该行,将密码变更同步到数据库
2. 次要问题:表单字段名可能不匹配
错误中的「两次密码不匹配」提示,大概率是模板里的表单字段name属性与PasswordChangeForm要求的不一致。PasswordChangeForm规定的新密码字段为new_password1和new_password2,若模板中写成new_password等其他名称,POST提交的数据无法匹配表单字段,就会出现该错误。
检查模板:确保字段name与payload键一致:
<form method="post"> {% csrf_token %} <!-- 推荐使用form.as_p自动渲染,避免手动写错字段名 --> {{ form.as_p }} <!-- 手动渲染时需保证name正确 --> <!-- <input type="password" name="old_password" required> --> <!-- <input type="password" name="new_password1" required> --> <!-- <input type="password" name="new_password2" required> --> <button type="submit">修改密码</button> </form>
修正后的完整测试代码
def test_profile_change_password(self): self.client.force_login(self.user) # 修改密码后必须保存到数据库 self.user.set_password(self.password) self.user.save() new_password = random_password() payload = { "old_password": self.password, "new_password1": new_password, "new_password2": new_password, } form = PasswordChangeForm(user=self.user, data=payload) self.assertTrue(form.is_valid()) response = self.client.get( reverse_lazy("researcher_ui:change_password"), ) self.assertEqual(response.template_name, ["researcher_UI/change_password.html"]) response = self.client.post( reverse_lazy("researcher_ui:change_password"), payload ) # 验证密码修改成功后的重定向(通常为302状态码) self.assertEqual(response.status_code, 302) # 刷新用户实例并验证密码确实被修改 self.user.refresh_from_db() self.assertTrue(self.user.check_password(new_password))
内容的提问来源于stack exchange,提问作者HenryM

