Best practices for writing functional tests to exercise forms?

109 views
Skip to first unread message

Tim Chase

unread,
Nov 4, 2015, 11:38:39 AM11/4/15
to django...@googlegroups.com
Beginning a new project, I'd like to include functional testing in
addition to my unit tests. What are recommended best practices for
exercising form-classes? I was hoping to do something like

c = django.test.Client()
results = c.get(page_url)
frm = SomeForm()
# pseudo-code for desired functional testing follows
self.assertFormIn(frm, results) # check the form is used
frm["name"] = "Pat Smith"
frm["email"] = "psm...@example.com"
results = c.post(page_url, frm) # submit the filled-out form
# check results for error messages and/or success here

Is there some piece of Django that I've missed that would facilitate
higher-level tests like this?

Thanks,

-tkc



Dheerendra Rathor

unread,
Nov 4, 2015, 12:34:38 PM11/4/15
to django...@googlegroups.com
You can use unittest.TestCase.assertRegexpMatches for checking if form is present in rendered template.
If you want to check the entire cycle (form loading, filling) you can use selenium for testing on live server. 

--
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users...@googlegroups.com.
To post to this group, send email to django...@googlegroups.com.
Visit this group at http://groups.google.com/group/django-users.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/20151104103632.38e014a4%40bigbox.christie.dr.
For more options, visit https://groups.google.com/d/optout.

Gergely Polonkai

unread,
Nov 4, 2015, 12:40:21 PM11/4/15
to Django users

An even more fine grained solution may be using an HTML parser like beautifulsoup. However, if your site's functionality depends on client side features like JavaScript, Selenium may be a better alternative.

Tim Chase

unread,
Nov 4, 2015, 4:57:39 PM11/4/15
to django...@googlegroups.com
On 2015-11-04 18:39, Gergely Polonkai wrote:
> An even more fine grained solution may be using an HTML parser like
> beautifulsoup. However, if your site's functionality depends on
> client side features like JavaScript, Selenium may be a better
> alternative.

My code doesn't rely on any JavaScript and I was hoping to keep
everything to mostly-Django without the need to drive an actual
web-browser like Selenium would do. Sorry, should have mentioned
that in my initial post.

My hope was for

1) something like "assertFormUsed()" akin to "assertTemplateUsed()"
to check whether a form was rendered as part of a given template,

and

2) the ability for to know what a form/formset's rendered field-names
would be so I don't have to hard-code them in the post(). When using
a form-set, the field-names get augmented with the form-set ID/number
which can make it more of a challenge to identify the field-names.
If there's some other functionality that would help with this instead
of how I was imagining it, I'm open to suggestions there.

Thanks,

-tkc


Carl Meyer

unread,
Nov 5, 2015, 7:10:26 PM11/5/15
to django...@googlegroups.com
Hi Tim,
I recommend the WebTest package [1], along with django-webtest [2] to
adapt it for use with Django.

It would replace your use of the Django test client (which isn't a
problem; it's a replacement with more features); you'd create and use a
WebTest client instead. It will parse the HTML of a response and pull
out any forms, allowing you to programmatically "fill out" the form
fields present in the markup and submit them. This allows for
integration tests that will catch errors like forgetting to include a
formset's management form in your template.

I use WebTest rather than the Django test client for all my
request/response integration tests.

Carl

[1] https://pypi.python.org/pypi/WebTest
[2] https://pypi.python.org/pypi/django-webtest

signature.asc

Tim Chase

unread,
Nov 8, 2015, 2:23:13 PM11/8/15
to django...@googlegroups.com
On 2015-11-06 01:09, Carl Meyer wrote:
> I recommend the WebTest package [1], along with django-webtest [2]
> to adapt it for use with Django.
>
Thanks for your suggestions, Carl. I'll check them out as it sounds
like they do at least some of what I want to do.

-tkc





Tim Chase

unread,
Nov 9, 2015, 2:53:40 PM11/9/15
to django...@googlegroups.com
On 2015-11-06 01:09, Carl Meyer wrote:
Just to follow up, django-webtest has been pretty much exactly what I
was looking for. Thanks!

-Tim



Tim Chase

unread,
Nov 13, 2015, 12:09:13 PM11/13/15
to django...@googlegroups.com
Monkeying around with my tests, I have the following snippet:

response = self.app.get(self.login_url)
login_form = response.forms["login"]
login_form["username"] = NAME
login_form["password"] = PASS
good_response = login_form.submit()

When I get my good_response back, it's a 302 redirect to "next". Is
there any way to get it follow that redirect automatically like
Django's Client allows for .get(..., follow=True)? Or do I have to
manually follow every redirect? I didn't see anything in the docs
for WebTest/django-webtest nor did I turn up anything
obvious-but-undocumented when poking around in the source for a
couple minutes.

Thanks,

-tkc




Carl Meyer

unread,
Nov 13, 2015, 12:23:24 PM11/13/15
to django...@googlegroups.com
Hi Tim,
As far as I know, you have to call `.follow()` on the response, which
follows the redirect and returns the next response. Is that what you
mean by "manually follow every redirect"? It's manual in a sense, but
there is a convenience method to make it very easy. You can just change
`login_form.submit()` to `login_form.submit().follow()`, you don't even
need another line of code.

I think this is better than the Django client's `follow=True`, because
it means your test specifies the expected number of redirects, and will
fail if the number of redirects changes unexpectedly. (You can check
this with Django's test client by using the `redirect_chain` attribute,
but usually people don't.)

Carl

signature.asc

Tim Chase

unread,
Nov 13, 2015, 12:51:27 PM11/13/15
to django...@googlegroups.com
On 2015-11-13 10:22, Carl Meyer wrote:
> As far as I know, you have to call `.follow()` on the response,
> which follows the redirect and returns the next response. Is that
> what you mean by "manually follow every redirect"? It's manual in a
> sense, but there is a convenience method to make it very easy. You
> can just change `login_form.submit()` to
> `login_form.submit().follow()`, you don't even need another line of
> code.

Okay, that's a nice balance between explicitly testing the redirect
and verbosity. I was afraid I'd have to do chainings something like

resp = frm.submit()
count = 0
while resp.code == 302 and count < 5: # or some broader test
resp = get(resp.redirect_url)
count += 1
if count >= 5:
did_we_circular_redirect("?")

for every one of my get/post calls. Using .follow() is much nicer.

Thanks,

-tkc



Reply all
Reply to author
Forward
0 new messages