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 (из официального репозитория книги) описывает исправление, соответствующее вашему вопросу:

https://github.com/hjwp/book-example/issues/8

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

Другие вопросы по тегам