Django 管理多对多(M2M)关系的视图如何进行单元测试
Django 多对多用户组视图单元测试解决方案
核心测试需求实现
你遇到的报错本质是操作了BoundField类型的表单字段对象,没有转为可用的校验对象,以下两种方案均可实现按列表校验用户归属:
方案1:直接校验表单QuerySet(推荐,单元测试优先)
无需解析HTML,直接校验视图传递给表单字段的查询集,效率高逻辑清晰:
def test_adm_update_group(self): self.client.force_login(self.user_staff.user) url = reverse("polls:adm_group_detail", args=[self.company.comp_slug, self.group1.id]) response = self.client.get(url) self.assertEqual(response.status_code, 200) # 获取两个选择框对应的查询集 all_users_qs = response.context["group_form"].fields["all_users"].queryset selected_users_qs = response.context["group_form"].fields["users"].queryset # 校验已加入group1的用户在右侧已选列表 self.assertIn(self.usr11, selected_users_qs) self.assertIn(self.usr12, selected_users_qs) # 校验未加入group1的用户在左侧待选列表 self.assertIn(self.usr13, all_users_qs) self.assertIn(self.usr14, all_users_qs) self.assertIn(self.usr21, all_users_qs) self.assertIn(self.usr22, all_users_qs) # 额外校验交叉不存在的情况,避免逻辑漏洞 self.assertNotIn(self.usr11, all_users_qs) self.assertNotIn(self.usr21, selected_users_qs)
方案2:校验渲染后的HTML(适合验证前端渲染结果)
将表单字段转为渲染后的HTML字符串,同时注意assertInHTML的参数顺序(第一个是要查找的内容,第二个是目标HTML):
def test_adm_update_group_html(self): self.client.force_login(self.user_staff.user) url = reverse("polls:adm_group_detail", args=[self.company.comp_slug, self.group1.id]) response = self.client.get(url) self.assertEqual(response.status_code, 200) # 把BoundField转为渲染后的HTML字符串 all_users_html = str(response.context["group_form"]["all_users"]) selected_users_html = str(response.context["group_form"]["users"]) # 校验用户在对应列表的HTML中 self.assertInHTML(self.usr11.user.username, selected_users_html) self.assertNotInHTML(self.usr11.user.username, all_users_html) self.assertInHTML(self.usr21.user.username, all_users_html) self.assertNotInHTML(self.usr21.user.username, selected_users_html)
增删用户功能测试示例
针对POST提交的增删逻辑,可以用以下用例校验:
def test_adm_group_modify_users(self): self.client.force_login(self.user_staff.user) url = reverse("polls:adm_group_detail", args=[self.company.comp_slug, self.group1.id]) # 构造提交数据:保留usr12,新增usr13、usr14,移除usr11 post_data = { "group_name": "Groupe 1", "weight": 40, "group_list": f"{self.usr12.id}-{self.usr13.id}-{self.usr14.id}" } # 提交POST请求 response = self.client.post(url, post_data) self.assertEqual(response.status_code, 200) # 校验数据库关联关系已更新 updated_group = EventGroup.objects.get(id=self.group1.id) self.assertIn(self.usr13, updated_group.users.all()) self.assertNotIn(self.usr11, updated_group.users.all()) # 校验返回页面的列表已更新 selected_qs = response.context["group_form"].fields["users"].queryset self.assertIn(self.usr13, selected_qs) self.assertNotIn(self.usr11, selected_qs)
选择框标题显示问题修复
你当前的列表容器用了d-flex默认横向排列,导致标题和选择框挤在同一行,修改两个列表容器的flex方向即可:
<!-- 左侧待选列表容器 --> <div class="col-md-5 d-flex flex-column align-items-center"> <p>Utilisateurs</p> {{ group_form.all_users}} </div> <!-- 右侧已选列表容器 --> <div class="col-md-5 d-flex flex-column align-items-center"> <p>Utilisateurs sélectionnés</p><br> {{ group_form.users }} <div class="control-group {%if group_form.users.errors %}error{%endif%}"></div> </div>
内容的提问来源于stack exchange,提问作者Christophe
相关产品推荐
相关产品推荐

