assertNotFormError in TestCase

There are situations when testing POST requests with the TestCase client where I pass (by mistake) the wrong fields in the data dictionary. In these cases, the validation fails and the route does not redirect as expected. Example:

    def test_admin_can_create_new_quotation(self):
    data = {
        "client": "wrong object type"
    }
    response = self.client.post(reverse("quote-create"), data=data)
    self.assertRedirects(response, "/quotes/1/") # Fails, status code 200

In these cases it would be helpful to see why the request failed, and I wrote for myself a tiny function, assertNotFormError, which takes the response, the form, and checks if the form has errors.

Is there something similar already available in Django test cases, and if not, would be of interest for the community to add this method to TestCase?

Thanks.

Valentino.

Hi @valentinogagliardi

Are you aware you can fetch the form from the response context and check its errors? Something like:

self.assertEquals(response.context['form'].errors['my_field'], ['This is not red'])

Also I’d advise against using tests like this to make assertions on your validation logic, since the test client is relatively slow, and that slowness will pile up over time. Instead, test all your validation via a form object directly. Then you can just have a few tests for the view that cover the get, post-fail, and post-success paths. For more information on this see my post on unit testing forms.

HTH,

Adam

Yes, this is exactly what I did. My concern is that these things needs to be tested either way, and I imagine a functional test will be even slower than the test client.

How? Such tests tend to be 10 or 100 times faster in my experience.

If I’m reading things correctly, I don’t think @adamchainz was suggesting a full on functional test at the browser level. I think he meant writing a form test using the form class directly.

If it were me, I’d probably write a happy path test using the client, then a bunch of form tests for the error cases.

def test_some_form_error():
    data = {'bad': 'data'}
    form = MyForm(data=data)

    is_valid = form.is_valid()

    assert not is_valid
    # Do some checks on form.errors

Worth explaining in detail my needs, someone else could find this useful.

My need is the reverse of what I’ve been suggested. I don’t want to test if a form has errors, instead, I want to test if the form has no errors.

Let’s say I have this test:

    def test_admin_can_create_new_quotation(self):
        data = {
            "client": self.regular_users[0].pk,
            "proposal_text": "This proposal does not include hosting.",
        }
        response = self.client.post(reverse("quote-create"), data=data)
        self.assertRedirects(response, "/quotes/1/")
        self.assertEqual(Quotation.objects.count(), 1)

Let’s also say I am missing some field in data. In this situation self.assertRedirects(response, "/quotes/1/") fails because the response returns 200, not 302.

At this point I have two options:

  • I get better at remembering things
  • I do something like self.assertNotFormError(response, "form")

This is the small utility I wrote:

    def assertNotFormError(self, response, form):
        if hasattr(response, "context_data"):
            errors = response.context_data[form].errors
            self.assertEqual(
                len(errors),
                0,
                msg=f"\nCannot assert that {form} has not errors. \nGot: {errors}",
            )

Now, I understand HTML and forms in templates are a concern for a set (even small) of functional tests, but I was just too lazy and didn’t want to install Selenium or Cypress, and wanted to see what’s possible with Django test client.

Thanks a lot!