How do you unit test formsets in Django?
Asked Answered
S

3

8

Ok, so i need to unit test a view, more precise form in a view . So i create such a unit test.

class ViewTest(TestCase):
    fixtures = ['fixture.json']
    def setUp(self):
        self.client = Client()
    def test_company_create(self):
        post_data = {
            'form-0-user': '',
            'form-0-share': '',
            'form-TOTAL_FORMS': 1,
            'form-INITIAL_FORMS': 0,
            'form-MAX_NUM_FORMS': 10
        }
    resp = self.client.post('/company/create/', post_data)
    self.assertFormError (resp, 'shareholder_formset', 'share', 'This field is required.')
    self.assertFormError (resp, 'shareholder_formset', 'user', 'This field is required.')

Ofcourse i get back an error

AttributeError: 'ShareholderFormFormSet' object has no attribute 'fields'

Because formset has forms in it, not fields..... So what is the correct way to test a formset?

Septavalent answered 14/5, 2013 at 13:12 Comment(15)
Take a look at the similar question: https://mcmap.net/q/462420/-django-formset-unit-test.Paunchy
@Paunchy That question is similar only in the sense that it is also about formsets and unit-testing... @Septavalent What about just testing the ShareholderForm? It looks like that is what you are trying to do with your asserts anyways...Actinochemistry
You are both testing the View and the Form in this "unit" test. There's no need to do a POST to test a form. See the "Testing Forms" section in A Guide to Testing in Django #2Herne
@Herne Problem is, i have a form and a formset in one view. And basically when formset data and antoher form data passes default validation , in a view i compare data from those forms and if it does not match , then i manally in a view raise error. I do that only because there is no other way to compare data from different forms, in my case a form and i formset ... So yeah, if you don't compare data from different forms, and there is no need to test form in a view, in my case it does not work that way.Septavalent
@Paunchy Those are completely 2 different questions.Septavalent
@Septavalent I'm commenting, not providing an answer. ;)Herne
@Viktor, sure, that's why this is a comment. Sorry.Paunchy
@Actinochemistry It's not gonna work, because error i try to test is raised in a view , when i compare a form and a formset, and if they don't match i raise ValidationError...Septavalent
Well :) that's just sad...Septavalent
Well, as an option, you can take a more high level approach and test your view and formset via webtest, mechanize or selenium.Paunchy
@Paunchy yeah , that's true. Just hoped there is some easy way to do it via django :))). Anyway thanks.Septavalent
@Septavalent The error is usually raised by the form, not the view. They view simply displays the error unless you significantly changed things...Actinochemistry
@Actinochemistry Usually. But if you need to validate data between two different forms. Then how you'll go about it ? Wanna hear your version. The only way i found is two validate in a view. Maybe you can suggest something better.Septavalent
So the data from each form depends on data from the other and you compare that data in the view? I was under the assumption that each form is independent of the other. Otherwise you are not testing the formset anymore, you are really testing your view...Actinochemistry
I just posted an answer here that might help recreating a post data information: https://mcmap.net/q/462420/-django-formset-unit-testPakistan
H
2

That's a functional test (since you go through the view, request possibly the model if you save it, etc.).

For the forms, django-webtest is a lot easier to use; you won't have to worry about these details with it: https://pypi.python.org/pypi/django-webtest

Hardenberg answered 4/9, 2013 at 0:51 Comment(0)
P
1

Django now has implemented an assertFormsetError.

django-basetestcase has a feature that will let you test the formset apart from the view, not requiring a response.

formset = MyFormSet(formset_data)

self.formset_error_test(
    formset,
    form_index=3,
    field='my_field',
    message='My error message.'
)
Plebiscite answered 6/4, 2019 at 2:12 Comment(1)
Do not post a link to a tool or library as an answer. Demonstrate how it solves the problem in the answer itself. Do edit the answer, and flag for undeletion once you have added the demonstration.Trodden
S
0

As you point out, the form name argument in assertFormError is really just a key in response.context_data. The key you are using returns a list of forms in the formset. So as you discovered, it does not work with assertFormError.

One option is to use assertEqual and just do a direct comparison. Something like:

self.assertEqual(response.context_data[u'shareholder_formset'][form_index].errors['share'], 'This field is required.')

I would also like to mention that my IDE (PyCharm) helped a lot in figuring this out. I was working on a similar problem. Turning on the debugger, putting a break point after the call to post() and inspecting the response, gave the solution.

Superelevation answered 15/8, 2014 at 22:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.