Skip to main content

FormStorm (v1.0.0)

FormStorm is a Python library that easily creates unit tests for Django forms by defining valid/invalid values for each field. All combinations of the fields' predefined values are submitted to the form to validate each field and test for unintended interdependence between fields. In addition to testing single- and multi-field validation, FormStorm can also test single-field uniqueness constraints by double-submitting a valid submission and checking which fields become invalid on the 2nd submission.

Example:

Suppose we have a form to create a Book object. The book's name is mandatory, but the subtitle is optional. A FormTest is created that provides examples of valid and invalid values for each field:

from django.forms import ModelForm
from formstorm import FormTest, FormElement
from django.test import TestCase


class Book(models.Model):
    title = models.CharField(max_length=100, blank=False, null=False)
    subtitle = models.CharField(max_length=100, blank=True, default="")


class BookForm(ModelForm):
    class Meta:
        model = Book
        exclude = []


class BookFormTest(FormTest):
	form = BookForm
	title = FormElement(
		good = ["Moby Dick"],
		bad = [None, "", "A"*101],
	)
	subtitle = FormElement(
		good = [None, "", "or The Whale"],
		bad = ["A"*101]
	)


class BookTestCase(TestCase):
    def setUp(self):
        self.theBookFormTest = BookFormTest()

    def test_book_form(self):
        self.theBookFormTest.run()

When the FormTest runs, the form will be tested with every combination of each field's possible values. Namely, the form will be tested with these values:

title subtitle result
Moby Dick "" Valid
Moby Dick None Valid
None None Invalid
"" None Invalid
AA[...]AA None Invalid
None "" Invalid
"" "" Invalid
AA[...]AA "" Invalid
Moby Dick or The Whale Valid
None or The Whale Invalid
"" or The Whale Invalid
AA[...]AA or The Whale Invalid
Moby Dick AA[...]AA Invalid
None AA[...]AA Invalid
"" AA[...]AA Invalid
AA[...]AA AA[...]AA Invalid

Without something like FormStorm, you either have to tediously create test cases for each possible input value, or you have to just trust that the form behaves how you intend it to.

(A runnable implementation of the example above can be found in tests/minimalapp/.)

Advanced Example:

An example showing how to use different field types can be found in tests/fstestapp/test.py.

Basically, all fields work as above, with the exception of ForeignKey and Many2Many fields whose values must be specified with Q() objects. Also, example values for multi-valued fields (such as Many2Many) can be created with the every_combo() function which returns every combination of the Many2Many options.

Validating multi-field constraints can be tested by specifying the values (as a dictionary) along with the expected results. For example, if the "title" and "subtitle" fields can't have a combined length greater than 150 characters, we can test this constraint like so:

additional_values = [
    ({'title': "A"*100, 'subtitle': "A"*50}, True),
    ({'title': "A"*50, 'subtitle': "A"*100}, True),
    ({'title': "A"*100, 'subtitle': "A"*51}, False),
    ({'title': "A"*51, 'subtitle': "A"*100}, False),
]

Install:

pip install formstorm

TODO:

  • End-to-end testing (with Selenium): This is partially implemented, and most of the necessary FormStorm functions have been abstracted. Just need to subclass FormTest and fully implement.
  • Tests for DRF Serializers. "SerializerStorm"
  • Set up CI
  • Handle the obscure, "long tail" cases by implementing a framework to define tests for any type of constraint (such as multi-column uniqueness constraints). To do this, a "sub-test" would define one or more form submissions and the (boolean) result expected. Sub-tests would be combined with each other and with the standard combinatorial mix of good/bad values so that all fields are tested simultaneously.

A tentative definition of the sub-tests is below:

sub_tests = [
    { # Sub-test 1
        field_names=["field1","field2",..."fieldN"],
        submissions = [ Each sub-test consists of multilpe submissions.
            (  # Submission 1 
                value1,  # the value for field1
                value2,  # the value for field2
                ...
                valueN,  # the value for fieldN
                result,  # the expected result of the submission
            ),
            (...), # Submission 2
            ...
            (...)  # Submission N
        ]
    },
    {...}, # Sub-test 2
    ...
    {...}  # Sub-test N
]

For example, suppose a model has two fields that have a multi-column uniqueness constraint:

class SomeModel(models.Model):
    field1 = models.TextField()
    field2 = models.TextField()
    field3 = models.TextField()
    ...
    fieldN = models.TextField()

    class Meta:
        constraints = [
            UniqueConstraint(
                fields=['field1', 'field2'], name='unique_together'
            )
        ] 

The sub-test to test this constraint would be defined like this:

class SomeModelFormTest(FormTest):
	form = SomeModelForm
    sub_tests = [
        {
            field_names=["field1","field2"],
            submissions = [
                ("a","a", True),
                ("a","b", True),  # Duplicate values in one column are fine
                ("b","a", True),  # ... as are duplicates in the other column
                ("a","a", False)  # ... but the same values again should fail
            ]
        }
    ]
    field3 = FormElement(good=[...], bad=[...])
    ...
    fieldN = FormElement(good=[...], bad=[...])

The advantage of this is that we can define a test only for the fields affected by a constraint, and have values for the other fields supplied by the normal good/bad value tests.

Release files for formstorm 1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for formstorm 1.0
File Size Uploaded
formstorm-1.0.tar.gz 8.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for formstorm 1.0
File Interpreter ABI Platform
formstorm-1.0-py3-none-any.whl Python 3 none any Details

Total release size: 16.4 kB

Release files / formstorm-1.0.tar.gz

Download URL formstorm-1.0.tar.gz
Size 8.1 kB
Tags Source
SHA-256 checksum
How to use checksums
4572135af9f46b344a2436dad52028bf9a3236cbefe58b88a5eca5e27659290c
BLAKE2b-256 checksum
How to use checksums
b73517057824706c740580636efd88d16c857a746baba7e9b0fe8c79a309165d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/3.2.0 pkginfo/1.5.0.1 requests/2.24.0 setuptools/49.2.0 requests-toolbelt/0.9.1 tqdm/4.47.0 CPython/3.8.2

Release files / formstorm-1.0-py3-none-any.whl

Download URL formstorm-1.0-py3-none-any.whl
Size 8.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
250ff746bb9012b98e4547344e00e2ac66bf3472106a3e61e9022d4f971b6ec3
BLAKE2b-256 checksum
How to use checksums
2933ab9f1119370fa20244150f7569147bb6af9424ebc6d354d211389c69fde7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/3.2.0 pkginfo/1.5.0.1 requests/2.24.0 setuptools/49.2.0 requests-toolbelt/0.9.1 tqdm/4.47.0 CPython/3.8.2

Release history Release notifications | RSS feed

This release

1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page