Search code examples
pythondjangounit-testingdjango-formsdjango-testing

Form valid in unit test even though ValidationError was raised


I'm testing my view for handling invalid form data. In my test case I'm submitting form with missing field and expect view to handle it by displaying error message. Here is a relevant snippet from clean in my form:

Form:

def clean(self):
    first_name = self.cleaned_data.get('first_name', '')
    last_name = self.cleaned_data.get('last_name', '')

    if not first_name or not last_name:
        print('Form is invalid')
        # This is always executed.
        raise ValidationError('All fields are required.')

    # other stuff

ValidationError is always raised, however, form.is_valid() returns True nonetheless:

View:

form = forms.EditProfileForm(request.POST, instance=user)

if form.is_valid():
    print('Form is valid')
    # Update user data. 
    # Always executed even though error was raised.

This leads to test failing, since I'm asserting that form validation will fail:

Test Case:

def test_invalid_data(self):
    formdata = {
        'first_name': 'Bruno',
        'last_name': '', # Missing data.
        'email': '[email protected]',
        'password': 'metamorphosis'
    }

    request = self.factory.post('/accounts/profile', formdata)
    request.user = self.user
    setup_messages(request)
    response = ProfileView.as_view()(request)

    self.assertEqual(response.status_code, 200)
    self.assertNotEqual(self.user.first_name, formdata['first_name']) # Fails.
    self.assertNotEqual(self.user.last_name, formdata['last_name'])
    self.assertNotEqual(self.user.email, formdata['email'])
    self.assertNotEqual(self.user.username, formdata['email'])

Form validation works properly on a live server (tested "by hand").

It looks like ValidationError is ignored during TestCase evaluation.

*Test output:

.Form is invalid
F..Form is valid
.
======================================================================
FAIL: test_invalid_data (.tests.ProfileTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/apps/accounts/tests.py", line 114, in test_invalid_data
    self.assertNotEqual(self.user.first_name, formdata['first_name'])
AssertionError: 'Bruno' == 'Bruno'

Solution

  • You haven't shown your full view, but usually you do return redirect('/success-url/') after a successful post. If your validation error was ignored, then the view would redirect with status code 302, and your test would fail on the earlier line self.assertEqual(response.status_code, 200)

    You can access the form from the view with form = response.context['form']. If you check form.is_valid() or form.errors in your test, you will see that your ValidationError has not been ignored.

    Your issue is that your assertNotEqual checks are not testing what you think they are. When you validate a model form, the instance is modified. If you want to check whether the user has been modified in the database, you need to refresh it from the database first.

    self.user.refresh_from_db()