【问题标题】:Assertion Error (200 Success != 400 Bad Request) while unit testing a Dockerized Flask App对 Dockerized Flask 应用程序进行单元测试时出现断言错误(200 成功!= 400 错误请求)
【发布时间】:2018-08-12 15:58:03
【问题描述】:

好的,我已经为这个问题苦苦挣扎了 2 天。当我在浏览器的表单中手动发布数据时,一切正常,并且我收到了应该说“谢谢...”的 Flash 消息。在测试我的 Flask 应用程序时,此测试未通过,因为在我的 Flask 表单上发送发布请求时收到 400 Bad Request 错误。需要明确的是,我使用 Flask-Mail 和 WTForms 作为表单,我的应用程序是 dockerized 并且还运行 redis 和 celery。我对这些东西有点陌生,所以如果我的问题不够清楚,请善待并告诉我是否应该提供更详细的信息。谢谢,这是使用 py.test 测试时显示的相关代码和错误。对不起,我仍然不允许在 StackOverflow 上发布图片。

错误代码: Pytest Assertion Error

联系人/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)])

联系人/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)

联系人/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': 'foo@bar.com',
          'message': 'Test message from Flexio.'
        }
        response = client.post(url_for('contact.index'), data=form,
                               follow_redirects=True)
        assert_status_with_message(200, response, 'Thanks')

【问题讨论】:

  • Flask-WTF 使用 CSRF 保护,您的测试没有提供令牌。
  • 它是通过 Jinja2 在我的 HTML 模板中提供的,FlaskDebugToolbar 将 CSRF_Token 显示为 True。是否也需要在测试中提供?如果是这样,我该怎么做?

标签: python flask pytest http-status-code-400


【解决方案1】:

您的浏览器将使用GET 请求first 请求表单,因此已将CSRF token 作为cookie 和表单中的隐藏表单元素。当您随后提交表单时,CSRF 保护通过。

您的测试没有发出GET 请求,也没有使用此类请求发出的表单中的表单字段,因此您的POST 请求缺少cookie 和隐藏字段。

在测试中,您可以通过将 WTF_CSRF_ENABLED 参数设置为 False 来禁用 CSRF 保护:

app.config['WTF_CSRF_ENABLED'] = False

【讨论】:

  • 我刚刚写了一条评论,我解决了这个问题。但是你解释得比我好,我接受你的回答作为解决方案。非常感谢,如果我早点问这个问题,我可以节省 2 天的调试时间!
猜你喜欢
  • 1970-01-01
  • 2016-08-25
  • 2013-11-05
  • 1970-01-01
  • 1970-01-01
  • 2021-04-09
  • 1970-01-01
  • 1970-01-01
  • 2017-09-06
相关资源
最近更新 更多