[ACCEPTED]-Django unit testing for form edit-django-forms

Accepted answer
Score: 31

It depends what you are trying to test. I 24 would target your tests a bit more finely 23 than it sounds like you are doing.

If the 22 code you need to test is the form validation 21 logic, then I would simply instantiate the 20 form class directly in your tests, pass 19 it various data dictionaries and call .is_valid(), check 18 for the proper errors or lack thereof. No 17 need to involve HTML or HTTP requests.

If 16 it's view logic (which IMO should be minimized) that 15 you are testing, you will probably want 14 to use the test client, but you shouldn't 13 need to do multi-stage tests or very many 12 tests at this level. In testing view logic 11 I wouldn't scrape HTML (that's testing templates), I'd 10 use response.context to pull out the form 9 object from the context.

If what you want 8 to test is that the templates contain the 7 proper HTML to make the form actually work 6 (in order to catch errors like forgetting 5 to include the management form for a formset 4 in the template), I use WebTest and django-webtest, which parse 3 your HTML and make it easy to fill in field 2 values and submit the form like a browser 1 would.

Score: 27

You can use response.context and form.initial to get the values you 1 need to post:

update_url = reverse('myobject_update',args=(myobject.pk,))

# GET the form
r = self.client.get(update_url)

# retrieve form data as dict
form = r.context['form']
data = form.initial # form is unbound but contains data

# manipulate some data
data['field_to_be_changed'] = 'updated_value'

# POST to the form
r = self.client.post(update_url, data)

# retrieve again
r = self.client.get(update_url)
self.assertContains(r, 'updated_value') # or
self.assertEqual(r.context['form'].initial['field_to_be_changed'], 'updated_value')
Score: 23

django-webtest is perfect for such tests:

from django_webtest import WebTest

class MyTestCase(WebTest):
    def test_my_view(self)
        form = self.app.get('/my-url/').form
        self.assertEqual(form['my_field_10'].value, 'initial value')
        form['field_25'] = 'foo'
        response = form.submit() # all form fields are submitted

In my opinion 8 it is better than twill for django testing 7 because it provides access to django internals 6 so native django's response.context, response.templates, self.assertTemplateUsed and self.assertFormError API is supported.

On 5 other hand it is better than native django 4 test client because it has much more powerful 3 and easy API.

I'm a bit biased ;) but I 2 believe that django-webtest is now the best 1 way to write django tests.

Score: 5

It's not clear but one guess is that you 13 have tests like this.

class TestSomething( TestCase ):
    fixtures = [ "..." ]
    def test_field1_should_work( self ):
        response= self.client.get( "url with form data already populated" )
        form_data = func_to_get_field( response )
        form_data['field1']= new value
        response= self.client.post( "url", form_data )
    def test_field2_should_work( self ):
        response= self.client.get( "url with form data already populated" )
        form_data = func_to_get_field( response )
        form_data['fields']= new value
        response= self.client.post( "url", form_data )

First, you're doing 12 too much. Simplify.

class TestFormDefaults( TestCase ):
    fixtures = [ "some", "known", "database" ]
    def test_get_should_provide_defaults( self ):
        response= self.client.get( "url with form data already populated" )

The above proves that 11 the defaults populate the forms.

class TestPost( TestCase ):
    fixtures = [ "some", "known", "database" ]
    def test_field1_should_work( self ):
        # No need to GET URL, TestFormDefaults proved that it workd.
        form_data= { expected form content based on fixture and previous test }
        form_data['field1']= new value
        response= self.client.post( "url", form_data )

Don't 10 waste time doing a "get" for each "post". You 9 can prove -- separately -- that the GET 8 operations work. Once you have that proof, simply 7 do the POSTs.

If you POSTS are highly session-specific 6 and stateful, you can still do a GET, but 5 don't bother parsing the response. You 4 can prove (separately) that it has exactly 3 the right fields.

To optimize your resting, consider 2 this.

class TestPost( TestCase ):
    fixtures = [ "some", "known", "database" ]
    def test_many_changes_should_work( self ):
        changes = [
            ( 'field1', 'someValue', 'some expected response' ),
            ( 'field2', 'someValue' ),
        for field, value, expected in changes:
            self.client.get( "url" ) # doesn't matter what it responds, we've already proven that it works.
            form_data= { expected form content based on fixture and previous test }
            form_data[field]= value
            response self.client.post( "url", form_data )
            self.assertEquas( expected, who knows what )

The above will obviously work, but 1 it makes the number of tests appear small.

Score: 1

Think carefully about why you need to unit-test 7 this. Forms are part of the core Django 6 functionality, and as such are very well 5 covered by Django's own unit tests. If all 4 you're doing is basic create/update, which 3 from your question it sounds like is the 2 case, I don't see any reason to write unit 1 tests for that.

Score: 0

You are possibly looking for tools that 7 do front end testing like twill or selenium

Both of these 6 generate python code, that can be included 5 within the django tests, so when you run 4 tests, it opens the urls, posts the data 3 and inspects whatever you want!

It should 2 help you to see these tests written for selenium, for 1 an open source reusable django app.

Score: 0

I don't see how or why you need unit tests 4 for this. Sounds to me like you're testing 3 for (and correcting) possible user input, which 2 is covered very simply with Django's form 1 validation (and model validation in 1.2)

Score: 0

I'd recommed you to take a look into acceptance 11 testing level tools like robot test framework 10 or letucce which are thought just for you 9 want to do "I'm verifying that my application 8 produces correct responses when a user makes 7 changes to a form", that sounds more like 6 acceptance (black-box) testing than unit-testing.

For 5 instace, Robot let you to define your tests 4 in tabular form, you define the workflow 3 once and then you can define easily a bunch 2 of tests that exercise the workflow with 1 different data.

More Related questions