Testing a Form Wizard in Django

I was writing a couple to tests form my django views and I got stuck with a Form Wizard. I tought.. well I will need to iterate over a form list and make several post as many steps as the wizard has. That assumetion was correct, so I created the data for every form, I create a form loop... and bump, It didn't work.

return handler(request, *args, **kwargs)
File "/home/marcos/.virtualenvs/platformv3/local/lib/python2.7/site-packages/formtools/wizard/views.py", line 284, in post code='missing_management_form',
ValidationError: [u'ManagementForm data is missing or has been tampered.']

 

It didn't work because like we can see in the Traceback a wizard also need management_form. Basically, I was makeing a post with the data for each form but of course teh Wizard needs to know wich step are you refering to. So, let's go to the real example.

Let's imagine that these are my Forms.

class TicketInfoForm(forms.Form):
    limit = forms.IntegerField(min_value=1)
    name = forms.CharField()
    pub_date = forms.DateTimeField()

class AddressForm(forms.Form):
    street_name = forms.CharField()
    zipcode = forms.CharField()
    city = forms.CharField()
    country = forms.CharField()

 

My views.py file

from django.shortcuts import redirect

from formtools.wizard.views import SessionWizardView

from .forms import TicketInfoForm, AddressForm

TICKETS_INFO_WIZARD_FORMS = (
    ("Ticket Information", TicketInfoForm),
    ("Address", AddressForm)
)

class TicketWizardFormView(SessionWizardView):
    template_name = 'tickets_info.html'

    def done(self, form_list, **kwargs):
        ticket_info_form, address_form = form_list

        address = address_form.save(commit=False)
        address.type = BUYER_ADDRESS
        address.save()

        tickets_info = tickets_info_form.save(commit=False)
        tickets_info.user = self.request.user
        tickets_info.address = address
        tickets_info.save()        

        return redirect(reverse('tickets_info_list'))

 

My urls.py file

from django.conf.urls import url, patterns
from django.contrib.auth.decorators import login_required

from .views import TicketsWizardFormView, TICKETS_INFO_WIZARD_FORMS

urlpatterns = patterns(
    ...
    url(r'^tickets-info/request/$', login_required(TicketsWizardFormView.as_view(TICKETS_INFO_WIZARD_FORMS, name='tickets_info_request'),
)

This code is working fine, with to test it we need to add and change some things.

  1. Every time that we make a POST besides of send the data that each form, we also need to send the current step: [wizard_name]-current_step: [current_step]
  2. We need to change the fields name to: [step_name]-[field_name]: [value]

For example for our TicketsInfo Form:

data_tickets_info_form = {
    'Ticket Information-limit': 10,
    'Ticket Information-name': 'My first Ticket Info',
    'Ticket Information-pub_data': '2017-11-30T12:00:00',
    'ticket_wizard_form_view-current_step': 'Ticket Information'
}

 

In the above example we can see that we have added the [current_step] value and each field has the [step_name] prefix.

Now we are ready to pass our test:

# test_views.py

from django.test import TestCase

from django.core.urlresolvers import reverse

from django.contrib.auth.models import User


class TestViews(TestCase):

    fixtures = ['users.json']

    def setUp(self):
        self.client = Client()
        self.user = User.objects.first()

    def test_ticket_wizard_form(self):
        name = 'Awesome Tickets'
        limit = 10

        data_ticket_form = {
            'Ticket Information-limit': limit,
            'Ticket Information-name': name,
            'Ticket Information-pub_data': '2020-11-30T12:00:00',
            'ticket_wizard_form_view-current_step': 'Ticket Information'
        }

        data_address_form = {
            'Address-street_name': 'Isac Newton',
            'Address-zipcode': '2011NA',
            'Address-city': 'Pythonic Straat',
            'Address-country': 'Netherlands',
            'ticket_wizard_form_view-current_step': 'Address'
        }

        TICKETS_STEPS_DATA = [data_ticket_form, data_address_form]

        for step, data_step in enumerate(TICKETS_STEPS_DATA, 1):
            response = self.client.post(reverse('tickets_info_request'), data_step)

            if step == len(TICKETS_STEPS_DATA):
                # make sure that after the create ticket we are redirected to Ticket List Page
                self.assertRedirects(response, reverse('tickets_info_list'))
            else:
                self.assertEqual(response.status_code, 200)

        # get the ticket
        TicketInfo.objects.get(
            name=name,
            limit=limit,
            user=self.user
        )

 

And... The test pass :-)

....................................
----------------------------------------------------------------------
Ran 36 tests in 5.555s

OK

Conclusion:

Test a Form Wizard is not so hard as seems at the beginning!!

Comments

Comments powered by Disqus