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

如何为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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 05:46:09