Testing if a view have an html element with at least one attribute

203 views
Skip to first unread message

ludovic coues

unread,
Jul 31, 2016, 6:39:49 AM7/31/16
to django...@googlegroups.com
Hello,

I am trying to test if a view is displaying a form with an input
element with name=username.

Currently, I have tried a lot of variation around
`self.assertContains( response, "<input name=\"username\">",
html=True)` but none work.
I assume that doesn't work because the view have a field input with
name=username but also autofocus="", a class and a bunch of other
attribute.

I could extract the form from the context and check the field username
from the form is displayed in the view but I'm not ok with that
solution. I'm testing if there is a input with name=username, not a
bunch of unrelated attribute.

I could also use a LiveServerTestCase and selenium but that look like
a bit overkill for my need. lxml is another option but it would bring
more dependencies.

A bit of help would be welcome :)



--

Cordialement, Coues Ludovic
+336 148 743 42

Andreas Kuhne

unread,
Jul 31, 2016, 7:22:52 AM7/31/16
to django...@googlegroups.com
--
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 https://groups.google.com/group/django-users.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/CAEuG%2BTah74hdZMv%2BwdZPPq5PLaJ%3DhxOFMNXuVLfFYSw2Uz4N0w%40mail.gmail.com.
For more options, visit https://groups.google.com/d/optout.

Django usually creates the inputs the same way, so what I do in my tests is to first dump the content of the response body on the screen and then copy the input statement from there. What you probably need is '<input name="username"', because it doesn't matter for your test if the html tag is closed. So you should be fine with:
self.assertContains(response, '<input name="username"')

ludovic coues

unread,
Jul 31, 2016, 7:57:53 AM7/31/16
to django...@googlegroups.com
First, thanks for the suggestion.

I just tried that, didn't work.
Here is the test file I used:


from django.test import TestCase

class HTMLTestCase(TestCase):

def test_input_in_fieldset(self):
fieldset = """
<fieldset class="form-group">
<input name="login">
<input autofocus="" class="form-control" id="id_username"
maxlength="254" name="username" rows="3" type="text" required />
</fieldset>
"""
self.assertInHTML('<input name=login>', fieldset)
self.assertInHTML('<input name="username">', fieldset)


First input is to have a working exemple, second is taken as is from
my view. Not closing the input in assertInHTML give an error `Couldn't
find '<input name="login"' in response`. I assume I would get the same
errors with assertContains(*args, html=True)

I could copy/past the input directly in my test but if the maxlength
or class attribute change, the test will break. Taking the input
directly from the django form will test if the django form is rendered
in the view, not if the view is displaying a suitable form.
> https://groups.google.com/d/msgid/django-users/CALXYUbmmotLwzjZY6ZZnAqy21xqZN1%3DiE7ah3g5JxFgEw-POZg%40mail.gmail.com.
> For more options, visit https://groups.google.com/d/optout.



Andreas Kuhne

unread,
Jul 31, 2016, 8:46:30 AM7/31/16
to django...@googlegroups.com

For more options, visit https://groups.google.com/d/optout.

Hi,

I think you have misunderstood me. 

First you need to check against the real response object, otherwise your test will only test if the item is present in your string, which is not what I meant. I meant that you should check what HTML django is generating to then get the correct information.

In your case, I am now guessing that the output of django is the information you put into the fieldset variable?

If so, you need to write
self.assertContains(response,'<input autofocus="" class="form-control" id="id_username" maxlength="254" name="username"')

The assertContains does not parse the html in any way, but uses a search for the text you entered. So if the text has 'autofocus="" class="form-control" id="id_username" maxlength="254"' before 'name="username"', then you need to add that as well, otherwise you won't find the text at all.

Regards,

Andréas


ludovic coues

unread,
Jul 31, 2016, 10:01:48 AM7/31/16
to django...@googlegroups.com
Oh, sorry. A bit of misunderstanding and miscommunication on my part.

The exemple I gave is just a quick way to reproduce my problem. The
real test use self.client, reverse, cast response.content to a string.
What I gave is a minimal exemple.

Also, I assumed you talked about comparing bit of html element, not
the raw content returned by the view. I'm quite uneasy to have the
test failing if a class is added or removed from the element. But yes,
your solution work for the current state of the application.
> https://groups.google.com/d/msgid/django-users/CALXYUbkp-oM3Y9dUYEcb8kbQT0kS95hDBqxVWMu8Cn-8iYQv3w%40mail.gmail.com.

Andreas Kuhne

unread,
Jul 31, 2016, 11:47:44 AM7/31/16
to django...@googlegroups.com

For more options, visit https://groups.google.com/d/optout.

Hi again,

I understand your problem with a test failing if a class is added, however, there is no other way to check this (as far as I know). If you want to explicitly check for an input with the name "username", I would probably try using xml. That way you could use xpath to search quickly.

Regards,

Andréas

ludovic coues

unread,
Jul 31, 2016, 12:02:18 PM7/31/16
to django...@googlegroups.com
Currently, I am using lxml. More dependencies but it's the cleanest
method I've found currently.
I use it like that:

from django.test import TestCase
from lxml import etree

class FormTest(TestCase):
def test_input(self):
response = self.client.get('/accounts/login')
self.assertEqual(response.status_code, 200)
doc = etree.HTML(response.content)
self.assertEqual(len(doc.findall('.//input[@name="username"]')), 1)
> https://groups.google.com/d/msgid/django-users/CALXYUbm5vcL%3Dz7qr7ZeS%2BMuNjH6LYscT9tr3Y3k0X3RLAAbEsQ%40mail.gmail.com.

Andreas Kuhne

unread,
Jul 31, 2016, 5:08:34 PM7/31/16
to django...@googlegroups.com
Jupp, thats about what I meant :-)

Probably the best way, if you don't want to check text.

Regards,

Andréas

Fred Stluka

unread,
Aug 3, 2016, 7:57:16 PM8/3/16
to django...@googlegroups.com
Ludovic,

On my project, we have lots of test cases that do a GET or POST and
then check the returned HTML.  We use the BeautifulSoup HTML
parser from our Django tests to avoid the types of errors you're
getting with simple string comparisons.  I'm not sure if there any
pros/cons vs lxml, but it works great for us!

--Fred

Fred Stluka -- mailto:fr...@bristle.com -- http://bristle.com/~fred/
Bristle Software, Inc -- http://bristle.com -- Glad to be of service!
Open Source: Without walls and fences, we need no Windows or Gates.

Reply all
Reply to author
Forward
0 new messages