Testing Wagtail Streamforms with reCaptcha

Wagtail Streamforms provides a very flexible way of adding editable forms to your Wagtail site. In this tutorial we will test some custom made functionality that we created.

July 15, 2020, 2:51 p.m.
Themes: Wagtail Streamfield options Testing

Our starting point is a Wagtail site in which we have implemented Wagtail Streamforms. We added a few things to the standard functionality, namely a reCaptcha field and an email that is sent after a form is submitted. That's what we'll test in this tutorial.

Contrary to earlier tutorials we will not use Factory Boy, firstly because we do not need a tree of Wagtail pages, secondly because the creation of a streamform factory is not easy. Instead we will copy the approach that Wagtail Streamforms uses itself in its tests.

Testing the reCaptcha field is pretty straightforward. Create a file /tests/test_streamforms.py and insert:

from ..wagtailstreamforms_fields import ReCaptchaField
from django.test import TestCase

class TestWagtailStreamforms(TestCase):

    def test_recaptcha_field(self):
        field = ReCaptchaField().get_formfield({})
        self.assertTrue(field.required)

We create an instance of a ReCaptchaField with the method get_formfield(). In the definition of our model we have overridden the get_options method to set the value of required to True; in this test we verify that that is indeed the case.

Testing the email submission in the file wagtailstreamforms_hooks.py that we have created is another matter. We need to create a form and simulate sending it via mail. For creating the form we will copy the approach in the tests of Wagtail Streamforms:

from wagtailstreamforms.models import Form
import json

def test_form(self):
    form = Form.objects.create(
        title="Form",
        template_name="streamforms/form_block.html",
        slug="form",
        fields=json.dumps(
            [
                {
                    "type": "singleline",
                    "value": {"label": "singleline", "required": True},
                    "id": "9c46e208-e53a-4562-81f6-3fb3f34520f2",
                },
                {
                    "type": "multifile",
                    "value": {"label": "multifile", "required": True},
                    "id": "91bac05f-754b-41a3-b038-ac7850e6f951",
                },
            ]
        ),
    )
    return form

The fields field is filled using the json.dumps method from an array of dictionaries with the contents of the form. Next we will define a method test_send_email, that will be similar to the method test_saves_record_with_files in the Wagtail Streamforms test, but which will rely on the functionality Django provides for testing email submission. The full code is as follows:

from ..wagtailstreamforms_hooks import email_submission
from django.core import mail
from django.core.files.uploadedfile import SimpleUploadedFile
from django.http import QueryDict

def test_send_email(self):
    instance = self.test_form()

    advancedformsetting = AdvancedFormSetting.objects.create(form=instance, to_address="mail@example.com")
    data_dict = {
        "singleline": 'text',
        "form_id": instance.pk,
        "form_reference": "some-ref",
    }
    uploadedfile = SimpleUploadedFile("file.mp4", b"file_content", content_type="video/mp4")
    files_dict = QueryDict(mutable=True)
    files_dict.update({"multifile": uploadedfile})
    files_dict.update({"multifile": uploadedfile})

    form_class = instance.get_form(data=data_dict, files=files_dict)

    assert form_class.is_valid()

    # Send message.
    email_submission(instance, form_class)

    # Test that one message has been sent.
    self.assertEqual(len(mail.outbox), 1)

    # Verify that the subject of the first message is correct.
    expected_subject = 'New Form Submission : %s' % instance.title
    self.assertEqual(mail.outbox[0].subject, expected_subject)

Up to the line assert form_class.is_valid() the code is almost equal to that of test_saves_record_with_files, with two exceptions. The first one is:

advancedformsetting = AdvancedFormSetting.objects.create(form=instance, to_address="mail@example.com")

We have created the class AdvancedFormSetting in our models to be able to edit in our admin the to-address of the mail with the submitted form. We need to create an instance here, so that Wagtail Streamforms will pick that up when creating the form class. The second change is the line:

uploadedfile = SimpleUploadedFile("file.mp4", b"file_content", content_type="video/mp4")

which is the code behind self.get_file() in the code of test_saves_record_with_files (inserted so that we don't have to introduce a separate TestCase class as in the tests of Wagtail Streamforms itself).

Now the email submission itself. To send the mail, we simply call the method email_submission that we have defined ourselves and want to test. The rest relies on Django's email testing functionality. Each outgoing email is saved in django.core.mail.outbox. So we can verify the contents of that mailbox: check that there is one message there and that the subject of the message is as we have defined in our method.

Let's run the test:

python3 manage.py test cms.tests.test_streamforms

We can check the coverage of all the tests of our cms app with:

coverage run --source=cms manage.py test cms
coverage html

The file index.html shows that we have a coverage of 97% and covered the most relevant lines of code. So we conclude our testing. Time to put our application online!

Comment on this article (sign in first or confirm by name and email below)