【问题标题】:pytest-django: Is this the right way to test view with parameters?pytest-django:这是用参数测试视图的正确方法吗?
【发布时间】:2019-06-08 23:27:44
【问题描述】:

假设我正在 Django 应用程序中测试 RSS 提要视图,我应该这样做吗?

def test_some_view(...):
    ...
    requested_url = reverse("personal_feed", args=[some_profile.auth_token])
    resp = client.get(requested_url, follow=True)
    ...
    assert dummy_object.title in str(resp.content)
  1. reverse-ing 然后将其传递给client.get() 是正确的测试方法吗?我认为它比简单地.get()URL 更干燥,更面向未来。

  2. 我应该以这种方式断言dummy_object 在响应中吗?

  3. 我在这里使用响应对象的str 表示进行测试。与使用 selenium 相比,何时执行此操作是一个好习惯?我知道它可以更容易地验证所述obj 或属性(如dummy_object.title)是否封装在H1 标记中。另一方面,如果我不关心 如何 表示 obj,那么像上面那样做会更快。

【问题讨论】:

  • 1.使用 reverse 是 IMO 正确的方法 - 您不是针对 URL 进行测试,而是针对按名称访问的 Django 视图进行测试。 2. AFAIK 内容是原始字节串;它应该首先使用resp.charset 解码。通常,我只在单元测试中测试 HTML 响应的状态码;内容最好在 e2e 测试中使用 selenium 进行测试,但对于简单的页面,您的检查也应该足够了。
  • @hoefling - 你能否扩展resp.charset 以及selenium 在这种情况下有何不同?我知道 selenium 的用途,并且它可以使测试涵盖 HTML 等...,但如果我只想测试某些 obj 是否在某些部分/QuerySet 中,这不节省时间与 @ 987654337@? (也许将此添加为答案,以便我可以标记它)
  • 啊,我没有仔细阅读您的问题 - 事实上,如果是 RSS 提要,我的评论毫无意义,因为它不是 text/html 回复 :-)
  • @hoefling - 不过,关于何时使用它与selenium 的一些见解将是黄金。我应该问一个不同的问题吗?还是这里合适?
  • 太棒了。我会稍微修改一下问题。

标签: python django pytest django-testing pytest-django


【解决方案1】:

重新评估我的评论(没有仔细阅读问题并忽略了 RSS 提要内容):

  1. reverse-ing 然后将其传递给client.get() 是正确的测试方法吗?我认为它比简单地.get()URL 更干燥,更面向未来。

我同意这一点 - 从 Django 的角度来看,您正在测试您的视图,而不关心它们映射到的确切端点。因此,使用reverse 是 IMO 明确且正确的方法。

  1. 我应该以这种方式断言dummy_object 在响应中吗?

你必须注意这里。 response.content 是一个字节串,所以断言dummy_object.title in str(resp.content) 是危险的。考虑以下示例:

from django.contrib.syndication.views import Feed

class MyFeed(Feed):
    title = 'äöüß'
    ...

urls注册订阅源:

urlpatterns = [
    path('my-feed/', MyFeed(), name='my-feed'),
]

测试:

@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

一个会失败,另一个不会因为正确的编码处理。

至于检查本身,我个人更喜欢将内容解析为一些有意义的数据结构,而不是使用原始字符串,即使是简单的测试也是如此。例如,如果您正在检查 text/html 响应中的数据,那么写入的开销不会太大

soup = bs4.BeautifulSoup(content, 'html.parser')
assert soup.select_one('h1#title-headliner') == '<h1>title</h1>'

root = lxml.etree.parse(io.StringIO(content), lxml.etree.HTMLParser())
assert next(root.xpath('//h1[@id='title-headliner']')).text == 'title'

不仅仅是

assert 'title' in content

但是,调用解析器更加明确(您不会意外测试 head 中页面元数据中的标题),并且还会对数据完整性进行隐式检查(例如,您知道有效负载确实是有效的 HTML因为解析成功)。

以您的示例为例:如果是 RSS 提要,我只需使用 XML 解析器:

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'

在这里,我使用的是 lxml,它是 stdlib 的 xml 的更快实现。将内容解析为 XML 树的优点还在于解析器从字节串中读取数据,并关注编码处理——因此您不必自己解码任何内容。

或者使用像 atoma 这样的高级别的东西,它是一个很好的 API,专门用于 RSS 实体,因此您不必与 XPath 选择器作斗争:

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'

  1. ...与使用 selenium 相比,何时执行此操作更好?

简短的回答 - 你不需要它。阅读您的问题时,我并没有过多关注,并且在撰写评论时考虑了 HTML 页面。关于这个 selenium 评论 - 这个库处理所有低级的东西,所以当测试开始累积时(通常,它们做得很快),写

uri = reverse('news-feed')
resp = client.get(uri)
root = parser.parse(resp.content)
assert root.query('some-query')

拖拽导入太麻烦了,所以selenium可以替换为

driver = WebDriver()
driver.get(uri)
assert driver.find_element_by_id('my-element').text == 'my value'

当然,使用自动化浏览器实例进行测试还有其他优势,例如准确查看用户在真实浏览器中看到的内容,允许页面执行客户端 javascript 等。当然,所有这些都主要适用于 HTML 页面测试;如果针对 RSS 提要进行测试,selenium 的使用是多余的,Django 的测试工具绰绰有余。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-05-29
    • 1970-01-01
    • 2013-02-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多