Search code examples
djangodjango-formsdjango-testing

Django testing a form, self.request kwargs gives keyerror


I have a modelform that uses __init__ to get kwargs. When running tests, I get a keyerror: 'requests'. For my unit test, I setup an existing user and existing object to test for duplicates per user. Here is the form and test:

class CourseForm(ModelForm):
    """ Form used for both creating a new course or updating an existing course."""
    class Meta:
        model = Course
        fields = ('course_name', 'grade_level',)

    def __init__(self, *args, **kwargs):
        self.request = kwargs.pop('request')
        self.qc = Course.objects.filter(user=self.request.user)
        super().__init__(*args, **kwargs)

    def clean(self):
        super(CourseForm, self).clean()
        course_name = self.cleaned_data.get('course_name')
        grade_level = self.cleaned_data.get('grade_level')
        # check exising course_name per user
        # exclude(pk=self.instance.pk) because when we update we don't have to consider the current name
        if course_name and self.qc.exclude(pk=self.instance.pk).filter(course_name__iexact=course_name).exists():
            raise ValidationError("A course with that name already exists.")

        return self.cleaned_data
class CourseFormTests(TestCase):
    @classmethod
    def setUp(self):
        self.user = CustomUser.objects.create_user(
            username='tester',
            email='[email protected]',
            password='tester123',
            is_teacher=True,
            is_active=True,
        )
        self.user.save()
        self.my_course = Course(user=self.user,
                                id='4d192045-07fa-477f-bac2-5a99fe2e7c04',
                                course_name="name",
                                grade_level="SEC")
        self.my_course.save()

    def test_CourseForm_valid(self):
        form = CourseForm(self.user, data={
            'user': self.user,
            'id': '4d192045-07fa-477f-bac2-5a99fe2e7c04',
            'course_name': "Science",
            'grade_level': "SEC"
        },)
        self.assertTrue(form.is_valid())

    def test_CourseForm_invalid_name_too_long(self):
        form = CourseForm(self.user, data={
            'user': self.user,
            'id': '4d192045-07fa-477f-bac2-5a99fe2e7c05',
            'course_name': "NameistoolongforthistobeOKNameistoolongforthistobeOK",
            'grade_level': "SEC"
        },)
        self.assertFalse(form.is_valid())

    def test_CourseForm_invalid_name_exists(self):
        form = CourseForm(self.user, data={
            'user': self.user,
            'id': '4d192045-07fa-477f-bac2-5a99fe2e7c05',
            'course_name': "name",
            'grade_level': "SEC"
        },)
        self.assertFalse(form.is_valid())

Here is a view that calls this form:

def courses(request):
    """view for listing and creating a course"""
    course_list = Course.objects.filter(
        user=request.user).order_by('course_name')
    context = {'course_list': course_list}
    user = request.user

    if request.method == 'POST':
        details = CourseForm(request.POST, request=request)

        if details.is_valid():
            if user.username != "Demo":
                course = details.save(commit=False)
                course.user = user
                course.save()

            form = CourseForm(request=request)
            context['form'] = form
            return render(request, "gradebook/courses.html", context)

        else:
            context['form'] = details
            return render(request, "gradebook/courses.html", context)

    else:
        form = CourseForm(request=request)
        context['form'] = form
        return render(request, "gradebook/courses.html", context)

urls.py
path('courses/', views.courses, name='courses'),

The exact error is:

line 107, in __init__
    self.request = kwargs.pop('request')
KeyError: 'request'

The form works in practice, but obviously doesn't pass the unit test.


Solution

  • Because your form requires the request in order to initialise, you can't just create an instance of it, you need to actually post your data as a request. You can do this with self.client.post, eg

    def test_CourseForm_invalid_name_exists(self):
        # we use client.post because the form requires a request in __init__()
        # if you need a logged in user, first use
        # self.client.login(username='john', password='johnpassword')
        response = self.client.post("/form/url/", data={
            'user': self.user,
            'id': '4d192045-07fa-477f-bac2-5a99fe2e7c05',
            'course_name': "name",
            'grade_level': "SEC"
        },)
        self.assertContains(response, "A course with that name already exists.", html=True)