Ошибка утверждения (200 успехов! = 400 неверных запросов) при модульном тестировании Dockerized Flask App

Хорошо, я борюсь с этой проблемой уже 2 дня. Когда я вручную отправляю данные в форму в браузере, все работает нормально, и я получаю флэш-сообщение, в котором должно быть написано «Спасибо...». При тестировании моего приложения Flask этот тест не проходит, потому что я получаю ошибку 400 Bad Request при отправке почтового запроса в моей форме Flask. Чтобы было ясно, я использую Flask-Mail и WTForms для формы, и мое приложение докеризовано, а также работает Redis и celery. Я новичок в этих вещах, поэтому, если мой вопрос недостаточно ясен, будьте любезны и сообщите мне, должен ли я предоставить более подробную информацию. Спасибо, а вот соответствующий код и ошибка, которая отображается при тестировании с помощью py.test. И извините за ссылки, мне все еще не разрешено размещать изображения на StackOverflow.

Код ошибки: Ошибка утверждения Pytest.

contact/forms.py:

from flask_wtf import FlaskForm
from wtforms import TextAreaField, StringField
from wtforms.validators import DataRequired, Length, Email


class ContactForm(FlaskForm):
    email = StringField("What's your e-mail address?",
                        [Email(), DataRequired(), Length(3, 254)])
    message = TextAreaField("What's your question or issue?",
                            [DataRequired(), Length(1, 8192)])

contact/views.py:

from flask import (
    Blueprint,
    flash,
    redirect,
    request,
    url_for,
    render_template)

from flexio.blueprints.contact.forms import ContactForm

contact = Blueprint('contact', __name__, template_folder='templates')


@contact.route('/contact', methods=['GET', 'POST'])
def index():
    form = ContactForm()

    if form.validate_on_submit():
        # This prevents circular imports.
        from flexio.blueprints.contact.tasks import deliver_contact_email

        deliver_contact_email(request.form.get('email'),
                                    request.form.get('message'))

        flash('Thanks, expect a response shortly.', 'success')
        return redirect(url_for('contact.index'))

    return render_template('contact/index.html', form=form)

contact/tasks.py:

from lib.flask_mailplus import send_template_message
from flexio.app import create_celery_app

celery = create_celery_app()


@celery.task()
def deliver_contact_email(email, message):
    """
    Send a contact e-mail.

    :param email: E-mail address of the visitor
    :type user_id: str
    :param message: E-mail message
    :type user_id: str
    :return: None
    """
    ctx = {'email': email, 'message': message}

    send_template_message(subject='[Flexio] Contact',
                          sender=email,
                          recipients=[celery.conf.get('MAIL_USERNAME')],
                          reply_to=email,
                          template='contact/mail/index', ctx=ctx)

    return None

lib/tests.py:

def assert_status_with_message(status_code=200, response=None, message=None):
    """
    Check to see if a message is contained within a response.

    :param status_code: Status code that defaults to 200
    :type status_code: int
    :param response: Flask response
    :type response: str
    :param message: String to check for
    :type message: str
    :return: None
    """
    assert response.status_code == status_code
    assert message in str(response.data)

tests/contact/test_views.py:

from flask import url_for
from lib.tests import assert_status_with_message
class TestContact(object):
    def test_contact_page(self, client):
        """ Contact page should respond with a success 200. """
        response = client.get(url_for('contact.index'))
        assert response.status_code == 200
    def test_contact_form(self, client):
        """ Contact form should redirect with a message. """
        form = {
          'email': '[email protected]',
          'message': 'Test message from Flexio.'
        }
        response = client.post(url_for('contact.index'), data=form,
                               follow_redirects=True)
        assert_status_with_message(200, response, 'Thanks')

person Goran Nushkov    schedule 12.08.2018    source источник
comment
Flask-WTF использует защиту CSRF, ваш тест не предоставляет токен.   -  person Martijn Pieters    schedule 12.08.2018
comment
Он предоставляется в моем шаблоне HTML через Jinja2, а панель FlaskDebugToolbar показывает CSRF_Token как True. Нужно ли предоставлять его и в тесте? И если да, то как я могу это сделать?   -  person Goran Nushkov    schedule 12.08.2018


Ответы (1)


Ваш браузер запросит форму с GET запросом first и, таким образом, получит CSRF-токен как файл cookie и как скрытый элемент формы в форме. Когда вы затем отправляете форму, защита CSRF проходит.

Ваш тест не делает запрос GET и не использует поля формы из формы, которую делает такой запрос, поэтому в вашем запросе POST отсутствуют как файл cookie, так и скрытое поле.

В тесте вы можете просто отключить защиту CSRF, установив для параметра WTF_CSRF_ENABLED значение False:

app.config['WTF_CSRF_ENABLED'] = False
person Martijn Pieters    schedule 12.08.2018
comment
Я просто написал комментарий, что исправил проблему. Но вы объяснили это лучше, чем я, и я принимаю ваш ответ как решение. Большое спасибо, я мог бы сэкономить 2 дня отладки, если бы задал вопрос раньше! - person Goran Nushkov; 12.08.2018