CRSF Token, мешающий TDD - есть ли переменная, в которой хранится вывод csrf?
Итак, я продолжал возвращать тест Failing в Django при сравнении ожидаемого и фактического HTML с вводом формы, поэтому я распечатал результат и понял, что разница была довольно простой строкой, вызванной моим {% csrf_token %}
, следующее:
<input type='hidden' name='csrfmiddlewaretoken' value='hrPLKVOlhAIXmxcHI4XaFjqgEAMCTfUa' />
Итак, я ожидаю простого ответа, но я не смог его найти: как мне отразить результат csrf_token для использования в тестировании?
Вот тестовая настройка и сбой:
def test_home_page_returns_correct_html_with_POST(self):
request = HttpRequest()
request.method = 'POST'
request.POST['item_text'] = 'A new list item'
response = home_page(request)
self.assertIn('A new list item', response.content.decode())
expected_html = render_to_string(
'home.html',
{'new_item_text': 'A new list item'},
******this is where I'm hoping for a simple one-line mapping******
)
self.assertEqual(response.content.decode(), expected_html)
Вот рендеринг из views.py:
def home_page(request):
return render(request, 'home.html', {
'new_item_text': request.POST.get('item_text'),
})
И вот провал теста, когда я запускаю тест с python manage.py test
FAIL: test_home_page_returns_correct_html_with_POST (lists.tests.HomePageTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "C:\Users\Me\PycharmProjects\superlists\lists\tests.py", line 29, in test_home_page_returns_correct_html_with_POST
self.assertEqual(response.content.decode(), expected_html)
AssertionError: '<!DO[298 chars] <input type=\'hidden\' name=\'csrfmiddlew[179 chars]tml>' != '<!DO[298 chars] \n </form>\n\n <table
id="id_list_t[82 chars]tml>'
----------------------------------------------------------------------
5 ответов
Судя по предоставленному вами фрагменту кода, похоже, вы работаете с примерами из книги "Разработка через тестирование на Python", но не используете Django 1.8.
В этом посте из обсуждения групп Google в книге рассматривается ошибка теста, которую вы испытываете:
https://groups.google.com/forum/#!topic/obey-the-testing-goat-book/fwY7ifEWKMU/discussion
И эта проблема GitHub (из официального репозитория книги) описывает исправление, соответствующее вашему вопросу:
Если можно, я хотел бы предложить лучший способ выполнения этого теста, используя встроенный тестовый клиент Django. Это будет обрабатывать все проверки CSRF для вас, а также будет проще в использовании. Это будет выглядеть примерно так:
def test_home_page_returns_correct_html_with_POST(self):
url = reverse('your_home_page_view_url_name')
response = self.client.post(url, {'item_text': 'A new list item'})
self.assertContains(response, 'A new list item')
Обратите внимание, что это также использует assertContains
, что является утверждением, предоставленным набором тестов Django.
Маркер CSRF является частью данных контекста шаблона, доступных вам, если вы используете классы Django TestCase:
response = self.client.get(url)
print(response.context)
https://docs.djangoproject.com/en/1.9/topics/testing/tools/
Ключ csrf_token
,
https://docs.djangoproject.com/en/1.9/_modules/django/template/context_processors/
РЕДАКТИРОВАТЬ: Как вы спросили, как вы можете сравнить HTML, отображаемый в вашем тесте с выводом вашего тестового сервера:
Потому что вы используете {% csrf_token %}
в вашем шаблоне вы не можете предоставить токен CSRF из контекста ответа render_to_string
метод, чтобы заставить его использовать то же значение. Вместо этого вам придется заменить его в результате render_to_string
, может быть, сначала ища входной элемент, используя селен (что делает сам тест). Однако насколько полезен этот тест, сомнительно. Это только поможет убедиться, что токен CSRF присутствует, но он все равно уже проверен на сервере в обычном режиме работы.
По сути, вы должны тестировать все, что вы оказываете непосредственное влияние в своем коде, а не что-либо, предлагаемое магией Джанго. Например, если вы делаете пользовательскую проверку формы, вы должны проверить это, а не проверку, предоставленную Django. Если вы изменяете наборы запросов (пользовательская фильтрация и т. Д.) В ListViews или get_object() в DetailViews, вы должны проверить, что получающиеся списки и 404 ошибки происходят в соответствии с вашим пользовательским кодом.
У меня была аналогичная проблема, поэтому я сделал функцию для возврата всех токенов csrf.
def test_home_page_returns_correct_html(self):
request = HttpRequest()
# Removes all the csrf token strings
def rem_csrf_token(string):
# Will contain everything before the token
startStr = ''
# Will contain everything after the token
endStr = ''
# Will carrry the final output
finalStr = string
# The approach is to keep finding the csrf token and remove it from the final string until there is no
# more token left and the str.index() method raises value arror
try:
while True:
# The beginning of the csrf token
ind = finalStr.index('<input type="hidden" name="csrfmiddlewaretoken"')
# The token end index
ind2 = finalStr.index('">', ind, finalStr.index('</form>'))
# Slicing the start and end string
startStr = finalStr[:ind]
endStr = finalStr[ind2+2:]
# Saving the final value (after removing one csrf token) and looping again
finalStr = startStr +endStr
except ValueError:
# It will only be returned after all the tokens have been removed :)
return finalStr
response = home_page(request)
expected_html = render_to_string('lists/home.html')
csrf_free_response = rem_csrf_token(response.content.decode())
self.assertEqual(csrf_free_response,
expected_html, f'{expected_html}\n{csrf_free_response}')
Я тоже столкнулся с этой проблемой (используя последние версии python 3.6.12 и django 1.11.29 согласно 2-му изданию книги).
Мое решение не отвечает на ваш вопрос "как визуализировать токен", но оно отвечает "как пройти тест, сравнивающий визуализированный шаблон с возвращенным ответом представления".
Я использовал следующий код:
class HomePageTest(TestCase):
def remove_csrf_tag(self, text):
'''Remove csrf tag from text'''
return re.sub(r'<[^>]*csrfmiddlewaretoken[^>]*>', '', text)
def test_home_page_is_about_todo_lists(self):
# Make an HTTP request
request = HttpRequest()
# Call home page view function
response = home_page(request)
# Assess if response contains the HTML we're looking for
# First read and open the template file ..
expected_content = render_to_string('lists/home.html', request=request)
print(len(response.content.decode()))
# .. then check if response is equal to template file
# (note that response is in bytecode, hence decode() method)
self.assertEqual(
self.remove_csrf_tag(response.content.decode()),
self.remove_csrf_tag(expected_content),
)
PS: Я основал это на этом ответе.