如何为Django表单编写单元测试?覆盖率及报错问题求助
解决Django视图单元测试的覆盖率问题与Profile不存在报错
我生成了覆盖率报告,发现views.py中的addreview和rating视图存在大量未覆盖代码。编写addreview视图测试时遇到customer.models.Profile.DoesNotExist: Profile matching query does not exist错误,希望能写出正确的单元测试来提升覆盖率并解决该报错。
相关代码
views.py
def addreview(request, u_id, shop_id): url = request.META.get('HTTP_REFERER') shop = shop_detail.objects.get(id=shop_id) user = Profile.objects.get(customer=u_id) if request.method == 'POST': form = ReviewForm(request.POST) if form.is_valid(): data = Review() data.review_text = form.cleaned_data['review_text'] data.review_rating = form.cleaned_data['review_rating'] data.shop = shop data.user = user data.save() return redirect(url) def rating(request): if not request.user.is_authenticated: return HttpResponseRedirect(reverse('customer_login')) if request.method == 'POST': form = RateUsForm(request.POST) if form.is_valid(): rate = RateUs() rate.rate_text = form.cleaned_data['rate_text'] rate.rating = form.cleaned_data['rating'] rate.user = request.user rate.save() return HttpResponseRedirect(reverse("index")) return render(request, 'shop/rate.html')
models.py
class Review(models.Model): user = models.ForeignKey(Profile,on_delete=models.CASCADE) shop = models.ForeignKey(shop_detail,on_delete=models.CASCADE) review_text = models.TextField(max_length=300) review_rating = models.IntegerField() def __str__(self): return f"{self.review_rating}" class RateUs(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE) rate_text = models.TextField() rating = models.IntegerField() def __str__(self): return f"{self.rating}"
forms.py
class ReviewForm(forms.ModelForm): class Meta: model= Review fields= ["review_text", "review_rating"] class RateUsForm(forms.ModelForm): class Meta: model= RateUs fields= ["rate_text","rating"]
现有rating测试用例
def test_valid_rating(self): rateus1 = RateUs.objects.first() data={ "user": rateus1.user, "rate_text": rateus1.rate_text, "rating": rateus1.rating } response = self.client.post(reverse('rating'), data=data) self.assertEqual(response.status_code, 302)
解决方案
1. 解决Profile不存在报错的核心思路
addreview视图通过Profile.objects.get(customer=u_id)获取Profile,测试时必须提前创建关联了User的Profile实例,否则会触发DoesNotExist异常。
2. 编写addreview视图的完整测试用例
假设测试类继承自django.test.TestCase,先在setUp方法中初始化测试数据:
from django.test import TestCase, Client from django.urls import reverse from django.contrib.auth.models import User from customer.models import Profile from shop.models import shop_detail, Review class AddReviewViewTest(TestCase): def setUp(self): self.client = Client() # 创建测试用户 self.user = User.objects.create_user(username='testuser', password='testpass123') # 创建关联的Profile self.profile = Profile.objects.create(customer=self.user) # 创建测试店铺(根据实际shop_detail字段调整) self.shop = shop_detail.objects.create(id=1, name='Test Shop') # 构造addreview的URL(假设URL名称是addreview) self.url = reverse('addreview', args=[self.user.id, self.shop.id]) # 设置HTTP_REFERER,避免视图中url为None self.client.defaults['HTTP_REFERER'] = reverse('shop_detail', args=[self.shop.id]) def test_addreview_post_valid_data(self): # 提交有效表单数据 data = { 'review_text': '这家店很棒!', 'review_rating': 5 } response = self.client.post(self.url, data=data) # 验证重定向到来源页 self.assertEqual(response.status_code, 302) self.assertEqual(response.url, self.client.defaults['HTTP_REFERER']) # 验证Review对象已创建 self.assertEqual(Review.objects.count(), 1) review = Review.objects.first() self.assertEqual(review.user, self.profile) self.assertEqual(review.shop, self.shop) self.assertEqual(review.review_text, '这家店很棒!') self.assertEqual(review.review_rating, 5) def test_addreview_post_invalid_data(self): # 提交无效数据(评分超出范围或评论为空) data = { 'review_text': '', 'review_rating': 6 } response = self.client.post(self.url, data=data) # 验证未创建Review对象 self.assertEqual(Review.objects.count(), 0) self.assertEqual(response.status_code, 200) def test_addreview_get_request(self): # 测试GET请求 response = self.client.get(self.url) self.assertEqual(response.status_code, 200) def test_addreview_nonexistent_profile(self): # 测试不存在的u_id invalid_url = reverse('addreview', args=[999, self.shop.id]) with self.assertRaises(Profile.DoesNotExist): self.client.post(invalid_url, data={'review_text': 'test', 'review_rating': 3}) def test_addreview_nonexistent_shop(self): # 测试不存在的shop_id invalid_url = reverse('addreview', args=[self.user.id, 999]) with self.assertRaises(shop_detail.DoesNotExist): self.client.post(invalid_url, data={'review_text': 'test', 'review_rating': 3})
3. 优化rating视图的测试用例,提升覆盖率
现有测试只覆盖了有效POST场景,补充其他分支测试:
from django.test import TestCase, Client from django.urls import reverse from django.contrib.auth.models import User from shop.models import RateUs class RatingViewTest(TestCase): def setUp(self): self.client = Client() self.user = User.objects.create_user(username='testuser', password='testpass123') self.url = reverse('rating') def test_rating_unauthenticated_user(self): # 测试未登录用户访问 response = self.client.get(self.url) self.assertEqual(response.status_code, 302) self.assertIn(reverse('customer_login'), response.url) def test_rating_get_request_authenticated(self): # 测试登录用户的GET请求 self.client.login(username='testuser', password='testpass123') response = self.client.get(self.url) self.assertEqual(response.status_code, 200) self.assertTemplateUsed(response, 'shop/rate.html') def test_rating_post_valid_data(self): # 测试登录用户提交有效数据 self.client.login(username='testuser', password='testpass123') data = { 'rate_text': '平台体验不错', 'rating': 4 } response = self.client.post(self.url, data=data) self.assertEqual(response.status_code, 302) self.assertEqual(response.url, reverse('index')) # 验证RateUs对象已创建 self.assertEqual(RateUs.objects.count(), 1) rate = RateUs.objects.first() self.assertEqual(rate.user, self.user) self.assertEqual(rate.rate_text, '平台体验不错') self.assertEqual(rate.rating, 4) def test_rating_post_invalid_data(self): # 测试登录用户提交无效数据 self.client.login(username='testuser', password='testpass123') data = { 'rate_text': '', 'rating': 0 } response = self.client.post(self.url, data=data) self.assertEqual(RateUs.objects.count(), 0) self.assertEqual(response.status_code, 200) self.assertTemplateUsed(response, 'shop/rate.html')
4. 额外建议
为避免视图出现未捕获的DoesNotExist异常,建议在addreview视图中添加异常处理:
from django.shortcuts import get_object_or_404 def addreview(request, u_id, shop_id): # 设置默认跳转页,避免HTTP_REFERER为空 url = request.META.get('HTTP_REFERER', reverse('index')) shop = get_object_or_404(shop_detail, id=shop_id) user = get_object_or_404(Profile, customer=u_id) # 后续逻辑不变
这样当shop或Profile不存在时,会返回404页面,测试时也可以验证404场景。
内容的提问来源于stack exchange,提问作者dxxper
相关产品推荐
相关产品推荐

