pytest-django: это правильный способ проверить представление с параметрами? - PullRequest
3 голосов
/ 09 июня 2019

Скажем, я тестирую представление RSS-канала в приложении Django, вот как мне это сделать?

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.С другой стороны, если меня не волнует , как представляется объект, это быстрее сделать, как указано выше.

1 Ответ

3 голосов
/ 10 июня 2019

Переоценка моего комментария (не внимательно прочитал вопрос и пропустил материал RSS):

  1. Является ли reverse -ing и затем передача его в client.get() правильным способом проверки? Я думал, что это СУХОЕ и более перспективно для будущего, чем просто .get() с помощью URL.

Я бы согласился с этим - с точки зрения Джанго, вы проверяете свои взгляды и не заботитесь о том, с какими точными конечными точками они сопоставляются. Таким образом, использование reverse является ИМО ясным и правильным подходом.

  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, при написании * 1036 это не намного увеличивает накладные расходы

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's 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 более чем достаточно.

...