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?

Hedde van der Heide
  • 21,841
  • 13
  • 71
  • 100
Viktor
  • 580
  • 2
  • 14
  • 29
  • Take a look at the similar question: http://stackoverflow.com/questions/1630754/django-formset-unit-test. – alecxe May 14 '13 at 14:32
  • @alecxe That question is similar only in the sense that it is also about formsets and unit-testing... @Viktor What about just testing the `ShareholderForm`? It looks like that is what you are trying to do with your asserts anyways... – Ngenator May 14 '13 at 14:33
  • 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 #2](http://toastdriven.com/blog/2011/apr/17/guide-to-testing-in-django-2/) – gertvdijk May 14 '13 at 14:39
  • @gertvdijk 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. – Viktor May 14 '13 at 14:51
  • @alecxe Those are completely 2 different questions. – Viktor May 14 '13 at 14:53
  • @Viktor I'm commenting, not providing an answer. ;) – gertvdijk May 14 '13 at 14:53
  • @Viktor, sure, that's why this is a comment. Sorry. – alecxe May 14 '13 at 14:53
  • @Ngenator 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... – Viktor May 14 '13 at 14:54
  • Well :) that's just sad... – Viktor May 14 '13 at 14:57
  • Well, as an option, you can take a more high level approach and test your view and formset via webtest, mechanize or selenium. – alecxe May 14 '13 at 14:58
  • @alecxe yeah , that's true. Just hoped there is some easy way to do it via django :))). Anyway thanks. – Viktor May 14 '13 at 15:30
  • @Viktor The error is usually raised by the form, not the view. They view simply displays the error unless you significantly changed things... – Ngenator May 14 '13 at 16:49
  • @Ngenator 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. – Viktor May 14 '13 at 23:08
  • 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... – Ngenator May 15 '13 at 00:03
  • I just posted an answer here that might help recreating a post data information: http://stackoverflow.com/a/38479643/1617295 – Raffi Jul 20 '16 at 11:12

3 Answers3

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

François Constant
  • 5,531
  • 1
  • 33
  • 39
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.'
)
Carl Brubaker
  • 1,602
  • 11
  • 26
  • 1
    Do not post a link to a tool or library as an answer. Demonstrate [how it solves the problem](http://meta.stackoverflow.com/a/251605) in the answer itself. Do [edit] the answer, and flag for undeletion once you have added the demonstration. – Bhargav Rao Apr 06 '19 at 02:17
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.

Chuck
  • 1,089
  • 1
  • 13
  • 18