4

我目前正在使用 Pytest 测试我的 Flask 应用程序,但遇到了 POST 请求和重定向的问题。让我再解释一下。

用户想要注册我们的新站点,但必须确认他们拥有不同站点的帐户。一旦他们确认了另一个帐户的凭据,他们就会被带到注册页面。如果他们来自确认页面,他们只能点击注册页面,否则他们会被重定向回主页。

我想测试这个功能,并且可以成功地向确认页面发出 POST 请求。如果我不指定follow_redirects=True并打印响应数据,我会得到以下 HTML:


  正在重定向...

  

正在重定向...

您应该被自动重定向到目标 URL:/register?emp_id=1。如果没有点击链接。

伟大的!正是我要找的!我想被重定向到注册页面。

现在,当我指定follow_redirects=True并打印出响应数据时,我希望注册页面 HTML 能够返回。响应数据改为返回主页 HTML。

我进一步调查了问题出在哪里。正如我之前提到的,您可以点击注册页面的唯一方法是从确认页面。我request.referrer在测试期间查看了视图中的属性,它会返回None。我尝试Referrer在测试的 POST 请求中设置标头内容,但没有成功。

这是我正在使用的代码:

视图.py

@app.route('/confirm', methods=['GET', 'POST'])
def confirm_bpm():
    if current_user.is_authenticated:
        return redirect(url_for('index'))
    form = BPMLoginForm()
    if form.validate_on_submit():
        bpm_user = BPMUser.query\
            .filter(and_(BPMUser.group_name == form.group_name.data,
                         BPMUser.user_name == form.username.data,
                         BPMUser.password == encrypt(form.password.data)))\
            .first()
        if not bpm_user:
            flash('BPM user account does not exist!')
            url = url_for('confirm_bpm')
            return redirect(url)
        if bpm_user.user_level != 3:
            flash('Only L3 Users can register through this portal.')
            url = url_for('confirm_bpm')
            return redirect(url)
        url = url_for('register', emp_id=bpm_user.emp_id)
        return redirect(url)
    return render_template('login/confirm_bpm.html', form=form)

@app.route('/register', methods=['GET', 'POST'])
def register():
    if current_user.is_authenticated:
        return redirect(url_for('index'))
    if request.method == 'GET' and\
            request.referrer != request.url_root + 'confirm':
        return redirect(url_for('index'))

    emp_id = request.args.get('emp_id')
    emp_id_exists = User.query.filter_by(emp_id=emp_id).first()
    if emp_id_exists:
        flash('User is already registered!')
        return redirect(url_for('login'))

    form = RegistrationForm()
    if form.validate_on_submit():
        new_user = User(login_type=form.login_type.data, login=form.login.data,
                        emp_id=emp_id)
        new_user.set_password(form.password.data)
        db.session.add(new_user)
        db.session.commit()
        flash('Registration successful!')
        return redirect(url_for('login'))
    return render_template('login/register.html', form=form)

测试

基础.py

from config import TestConfig
from app import app, db

@pytest.fixture
def client():
    """
    Initializes test requests for each individual test.  The test request
    keeps track of cookies.
    """
    app.config.from_object(TestConfig)

    client = app.test_client()
    ctx = app.app_context()
    ctx.push()

    yield client

    ctx.pop()


def confirm_bpm_login(client, group_name, username, password):
    """
    POST to /confirm
    """
    return client.post('/confirm', data=dict(
        group_name=group_name,
        username=username,
        password=password,
        submit=True
    ), follow_redirects=True)

test_auth.py

from app import db
from app.models import BPMCompany, BPMEmployee, User, BPMUser

from tests.base import client, db_data, login, confirm_bpm_login

def test_registration_page_from_confirm(client, db_data):
    """
    Test registration page by HTTP GET request  from "/confirm" url.
    Should cause redirect to registration page.

    !!! FAILING !!!
    Reason: The POST to /confirm will redirect us to /register?emp_id=1,
    but it will return the index.html because in the register view,
    request.referrer does not recognize the POST is coming from /confirm_bpm
    """
    bpm_user = BPMUser.query.filter_by(id=1).first()

    rv = confirm_bpm_login(client, bpm_user.group_name,
                           bpm_user.user_name, 'jsmith01')

    assert b'Register' in rv.data

db_data参数是base.py中的一个固定装置,它只是用注册流程工作所需的数据填充数据库。

我的目标是测试一个完整的注册流程,而不必将确认和注册分成两个测试。

4

1 回答 1

4

RefererFlask 测试客户端在跟随重定向时似乎没有添加标头。

您可以做的是实现您自己的以下重定向版本。也许是这样的:

def confirm_bpm_login(client, group_name, username, password):
    """
    POST to /confirm
    """
    response = client.post('/confirm', data=dict(
        group_name=group_name,
        username=username,
        password=password,
        submit=True
    ), follow_redirects=False)
    if 300 <= response.status_code < 400:
        response = client.get(response.headers['Location'], headers={
            "Referer": 'http://localhost/confirm'
        })
    return response

请测试一下,我是凭记忆写的,可能需要一些小调整。

于 2018-06-22T18:30:39.813 回答