Django - 测试功能视图的 URL 时是否需要所有相关模型?

Django - Are all relevent models needed when testing the URL of a functional view?

我有几个传递参数的功能视图。我正在尝试编写测试来检查 url、状态等。到目前为止,我只是从测试 URL 名称开始。我在视图中有一些查询集,从回溯来看,我需要为 setUp

中的查询定义对象

urls

app_name = 'gradebook'
urlpatterns = [     
    path('updatesinglegrade/<int:assess_pk>/<int:class_pk>/<int:grade_pk>/',
             views.updatesinglegrade, name='updatesinglegrade'),
]

查看

def updatesinglegrade(request, assess_pk, class_pk, grade_pk):
    grade = Grade.objects.get(id=grade_pk)
    gradescale = GradeBookSetup.objects.get(user=request.user)
    scale_choice = grade_scale_choice(gradescale.scale_mode)

    form = UpdateGradeForm(gradescale=scale_choice)
    context = {'form': form}
    context['grade'] = grade

    if request.method == 'POST':
        form = UpdateGradeForm(
            request.POST, gradescale=scale_choice)

        if form.is_valid():
            cd = form.cleaned_data
            grade.score = cd['score']
            grade.save()
            return redirect('gradebook:assessdetail', assess_pk, class_pk)

        else:
            return render(request, "gradebook/grade_single_form.html", context)

    else:
        return render(request, "gradebook/grade_single_form.html", context)

测试

class UpdateSingleGradeTests(TestCase):

    def setUp(self):
        self.user = CustomUser.objects.create_user(
            username='tester',
            email='tester@email.com',
            password='tester123'
        )
        login = self.client.login(username='tester', password='tester123')

    def test_updatesinglegrade_url_name(self):
        response = self.client.get(reverse('gradebook:updatesinglegrade', kwargs={
                                   'assess_pk': 1, 'class_pk': 2, 'grade_pk': 3}))
        self.assertEqual(response.status_code, 200)

回溯

======================================================================
ERROR: test_updatesinglegrade_url_name (gradebook.tests.UpdateSingleGradeTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\Users\Doug\OneDrive\django\gradebook\gradebook\tests.py", line 102, in test_updatesinglegrade_url_name
    response = self.client.get(reverse('gradebook:updatesinglegrade', kwargs={
  File "C:\Users\Doug\.virtualenvs\gradebook-6RQSg7dk\lib\site-packages\django\test\client.py", line 742, in get
    response = super().get(path, data=data, secure=secure, **extra)
  File "C:\Users\Doug\.virtualenvs\gradebook-6RQSg7dk\lib\site-packages\django\test\client.py", line 396, in get
    return self.generic('GET', path, secure=secure, **{
  File "C:\Users\Doug\.virtualenvs\gradebook-6RQSg7dk\lib\site-packages\django\test\client.py", line 473, in generic
    return self.request(**r)
  File "C:\Users\Doug\.virtualenvs\gradebook-6RQSg7dk\lib\site-packages\django\test\client.py", line 719, in request
    self.check_exception(response)
  File "C:\Users\Doug\.virtualenvs\gradebook-6RQSg7dk\lib\site-packages\django\test\client.py", line 580, in check_exception
    raise exc_value
  File "C:\Users\Doug\.virtualenvs\gradebook-6RQSg7dk\lib\site-packages\django\core\handlers\exception.py", line 47, in inner
    response = get_response(request)
  File "C:\Users\Doug\.virtualenvs\gradebook-6RQSg7dk\lib\site-packages\django\core\handlers\base.py", line 181, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "C:\Users\Doug\.virtualenvs\gradebook-6RQSg7dk\lib\site-packages\django\contrib\auth\decorators.py", line 21, in _wrapped_view
    return view_func(request, *args, **kwargs)
  File "C:\Users\Doug\.virtualenvs\gradebook-6RQSg7dk\lib\site-packages\django\contrib\auth\decorators.py", line 21, in _wrapped_view
    return view_func(request, *args, **kwargs)
  File "C:\Users\Doug\OneDrive\django\gradebook\gradebook\views.py", line 1030, in updatesinglegrade
    grade = Grade.objects.get(id=grade_pk)
  File "C:\Users\Doug\.virtualenvs\gradebook-6RQSg7dk\lib\site-packages\django\db\models\manager.py", line 85, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "C:\Users\Doug\.virtualenvs\gradebook-6RQSg7dk\lib\site-packages\django\db\models\query.py", line 435, in get
    raise self.model.DoesNotExist(
gradebook.models.Grade.DoesNotExist: Grade matching query does not exist.

----------------------------------------------------------------------
Ran 6 tests in 1.527s

FAILED (errors=1)

我可以创建一个 Grade 对象,如回溯所示,但我的 Grade 模型依赖于五个外键,其中一个有另一个外键:

型号

class Grade(models.Model):
    score = models.CharField(max_length=3, blank=True, default="INC")
    user = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
    assessment = models.ForeignKey(
        Assessment, on_delete=models.CASCADE, null=True, blank=True)
    objective = models.ForeignKey(
        Objective, on_delete=models.CASCADE, blank=True)
    student = models.ForeignKey(Student, on_delete=models.CASCADE)
    cblock = models.ForeignKey(Classroom, on_delete=models.CASCADE, default=1)
    time_created = models.DateField(
        auto_now=False, auto_now_add=False, default=timezone.now)

工作测试

from .models import CustomUser, Grade, Student, Course, Classroom, Objective, Assessment, GradeBookSetup

class UpdateSingleGradeTests(TestCase):

    def setUp(self):
        self.user = CustomUser.objects.create_user(
            username='tester',
            email='tester@email.com',
            password='tester123',
            is_teacher=True,
        )
        studentuser = CustomUser.objects.create(
            username='student_test'
        )
        stud = Student.objects.create(
            user=studentuser,
            student_number='111222'
        )
        course = Course.objects.create(
            user=self.user
        )
        classroom = Classroom.objects.create(
            user=self.user,
            course=course
        )
        # classroom.students.set(stud)
        objective = Objective.objects.create(
            user=self.user,
            course=course
        )
        assessment = Assessment.objects.create(
            user=self.user,
            course=course
        )
        # assessment.objectives.set(objective)
        login = self.client.login(username='tester', password='tester123')

        grade = Grade.objects.create(
            id=3,
            user=self.user,
            student=stud,
            objective=objective,
            cblock=classroom,
            assessment=assessment
        )

        gbs = GradeBookSetup.objects.create(
            id=1,
            user=self.user
        )

    def test_updatesinglegrade_url_name(self):
        response = self.client.get(reverse('gradebook:updatesinglegrade', kwargs={
            'assess_pk': 1, 'class_pk': 2, 'grade_pk': 3}))
        self.assertEqual(response.status_code, 200)

这是否意味着我需要在 setUp 中创建 6 个对象?我得到了这个测试的工作,我想知道这是否是我必须添加到我的许多 class TestCase(涉及类似 objects/models)的东西?或者也许有更有效的方法来做到这一点?

您可以创建夹具并在您的测试用例中使用它class

关于主题的文档:https://docs.djangoproject.com/en/3.2/topics/testing/tools/#fixture-loading

class UpdateSingleGradeTests(TestCase):
    fixtures = ['fixture.json']

简短的回答是,是的,非空 ForeignKey 字段必须在测试中填充,因为它们是非空的。

有几种方法可以做到这一点:

  1. 对每个模型调用 objects.create()。这可能非常乏味且耗时。

  2. 使用一个fixture。创建 JSON 文件也很繁琐,但幸运的是,您可以使用 Django admin 进行数据输入,然后 ./manage.py dumpdata 生成 JSON.

  3. 使用第 3 方库,例如 factory-boymodel-bakery。这些库可以为除了具有价值之外对测试无关紧要的字段生成随机数据。如果您需要某个测试的特定值,那么您可以提供它。