Reevaluating my comment (didn't carefully read the question and overlooked the RSS feed stuff):
- Is
reverse
-ing and then passing that into the client.get()
the right way to test? I thought it's DRYer and more future-proof than simply .get()
ing the URL.
I would agree on that - from Django point, you are testing your views and don't care about what the exact endpoints they are mapped against. Using reverse
is thus IMO the clear and correct approach.
- Should I assert that
dummy_object
is in the response this way?
You have to pay attention here. response.content
is a bytestring, so asserting dummy_object.title in str(resp.content)
is dangerous. Consider the following example:
from django.contrib.syndication.views import Feed
class MyFeed(Feed):
title = 'äöüß'
...
Registered the feed in urls
:
urlpatterns = [
path('my-feed/', MyFeed(), name='my-feed'),
]
Tests:
@pytest.mark.django_db
def test_feed_failing(client):
uri = reverse('news-feed')
resp = client.get(uri)
assert 'äöüß' in str(resp.content)
@pytest.mark.django_db
def test_feed_passing(client):
uri = reverse('news-feed')
resp = client.get(uri)
content = resp.content.decode(resp.charset)
assert 'äöüß' in content
One will fail, the other won't because of the correct encoding handling.
As for the check itself, personally I always prefer parsing the content to some meaningful data structure instead of working with raw string even for simple tests. For example, if you are checking for data in a text/html
response, it's not much more overhead in writing
soup = bs4.BeautifulSoup(content, 'html.parser')
assert soup.select_one('h1#title-headliner') == '<h1>title</h1>'
or
root = lxml.etree.parse(io.StringIO(content), lxml.etree.HTMLParser())
assert next(root.xpath('//h1[@id='title-headliner']')).text == 'title'
than just
assert 'title' in content
However, invoking a parser is more explicit (you won't accidentally test for e.g. the title in page metadata in head
) and also makes an implicit check for data integrity (e.g. you know that the payload is indeed valid HTML because parsed successfully).
To your example: in case of RSS feed, I'd simply use the XML parser:
from lxml import etree
def test_feed_title(client):
uri = reverse('my-feed')
resp = client.get(uri)
root = etree.parse(io.BytesIO(resp.content))
title = root.xpath('//channel/title')[0].text
assert title == 'my title'
Here, I'm using lxml
which is a faster impl of stdlib's xml
. The advantage of parsing the content to an XML tree is also that the parser reads from bytestrings, taking care about the encoding handling - so you don't have to decode anything yourself.
Or use something high-level like atoma
that ahs a nice API specifically for RSS entities, so you don't have to fight with XPath selectors:
import atoma
@pytest.mark.django_db
def test_feed_title(client):
uri = reverse('my-feed')
resp = client.get(uri)
feed = atoma.parse_atom_bytes(resp.content)
assert feed.title.value == 'my title'
- ...When is it a good practice to do this vs. using
selenium
?
Short answer - you don't need it. I havent't paid much attention when reading your question and had HTML pages in mind when writing the comment. Regarding this selenium
remark - this library handles all the low-level stuff, so when the tests start to accumulate in count (and usually, they do pretty fast), writing
uri = reverse('news-feed')
resp = client.get(uri)
root = parser.parse(resp.content)
assert root.query('some-query')
and dragging the imports along becomes too much hassle, so selenium
can replace it with
driver = WebDriver()
driver.get(uri)
assert driver.find_element_by_id('my-element').text == 'my value'
Sure, testing with an automated browser instance has other advantages like seeing exactly what the user would see in real browser, allowing the pages to execute client-side javascript etc. But of course, all of this applies mainly to HTML pages testing; in case of testing against the RSS feed selenium
usage is an overkill and Django's testing tools are more than enough.
reverse
is IMO the correct way - you are testing not against URLs, but against Django views, which are being accessed by name. 2. AFAIK the content is a raw bytestring; it should be decoded usingresp.charset
first. Usually, I only test status codes of HTML responses in unit tests; the content is better to test withselenium
in e2e tests, but for simple pages your check should also suffice. – Abutresp.charset
and how isselenium
different in this context? I know what selenium is used for and that it can make the test cover HTML, etc..., but in case I just want to test whether someobj
is in some part/QuerySet, doesn't this save time vsselenium
? (maybe add this as an answer so I can mark it) – Foliagetext/html
response :-) – Abutselenium
would be golden. Should I ask a different question? or is it suitable here? – Foliage