pytest-django:这是使用参数测试视图的正确方法吗?

时间:2019-06-08 23:27:44

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

说我要在Django应用中测试RSS feed视图,这是我应该怎么做吗?

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是否正确,然后将其传递到client.get(),是正确的测试方法?我认为它比以前仅.get()设置URL更干燥,而且更适合未来。

  2. 我是否应该断言dummy_object是这种方式的响应?

  3. 我在这里使用响应对象的str表示形式进行测试。与使用selenium相比,什么时候是个好习惯?我知道,这样可以更轻松地验证所说的obj或属性(如dummy_object.title)是否封装在H1标记内。另一方面,如果我不关心如何表示obj,则像上面那样做起来会更快。

1 个答案:

答案 0 :(得分:2)

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

  
      
  1. reverse是否正确,然后将其传递到client.get()进行测试的正确方法?我认为它比以前仅.get()设置URL更干燥,并且更具前瞻性。
  2.   

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

  
      
  1. 我是否应该断言dummy_object是这种回复方式?
  2.   

您必须在这里注意。 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 feed,我将仅使用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的高级功能,它是专门用于RSS实体的不错的API,因此您不必与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相比,什么时候是个好习惯?
  2.   

简短的答案-您不需要它。在阅读您的问题时,我并没有特别注意,在撰写评论时,请记住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的测试工具就足够了。