Testing with Rebar

Rebar includes some helpers for writing unit tests for Django Forms.

Generating Form Data

A common pattern for testing Forms and Formsets is to create a dict of form data to use when instantiating the form. When dealing with a large form, or a form with lots of initial values, keeping the test in sync can be cumbersome.

rebar.testing.flatten_to_dict() takes a Form, Formset, or FormGroup, and returns its fields as a dict. This allows you to create an unbound form, flatten it, and use the resulting dict as data to test with.

If passed a FormSet, the return of flatten_to_dict will include the ManagementForm.

For example, if you have a form with a required field:

from django import forms

class NameForm(forms.Form):

    first_name = forms.CharField(required=True)

You can create an unbound version of the form to generate the form data dict from.

>>> from rebar.testing import flatten_to_dict
>>> unbound_form = NameForm()
>>> form_data = flatten_to_dict(unbound_form)
>>> form_data['first_name'] = 'Larry'
>>> NameForm(data=form_data).is_valid() is True

This is an obviously oversimplified example, but flatten_to_dict allows you to focus your test on the fields that actually matter in that context.

If a ModelForm is passed to flatten_to_dict with a foreign key, the related object’s primary key, if any, will be used as the value for that field. This correlates with how Django treats those fields in form proessing.

Empty Form Data for Formsets

FormSets allow you to create a view that contains multiple instances of the same form. FormSets have a convenience property, empty_form, which returns an empty copy of the form, with its index set to the placeholder __prefix__.

Rebar provides a convience function, rebar.testing.empty_form_data(), which takes the empty form and returns the form data dict with the prefix correctly filled in. For example, if the FormSet contains a single item, the prefix will be set to 2.

For example, assume we make a FormSet from our example NameForm above.

from django.forms import formsets
NameFormSet = formsets.formset_factory(form=NameForm)

When we instantiate that FormSet, it will have a single form in it, which is empty (ie, we didn’t start with any forms). That forms prefix contains “1”, indicating its place in the sequence.

>>> formset = NameFormSet()
>>> len(formset)
>>> formset[0].prefix

The empty_form property contains a copy of the NameForm, with its prefix set to the __prefix__ sentinel.

>>> formset.empty_form  
<NameForm ...>
>>> formset.empty_form.prefix

If we pass the FormSet to empty_form_data, we’ll get a dict of data for the next form in the sequence.

>>> from rebar.testing import empty_form_data
>>> empty_form_data (formset)
{u'form-1-first_name': None}

You can also specify a specific index for the generated form data.

>>> empty_form_data (formset, index=42)
{u'form-42-first_name': None}