Pytest Flask 请求来源(Referrer)

4

我目前正在使用Pytest测试我的Flask应用程序,并遇到了一个关于POST请求和重定向的问题。让我更详细地解释一下。

用户想要注册我们的新网站,但必须确认他们在另一个网站上有账户。一旦他们确认了其他帐户的凭据,他们就会被带到注册页面。只有从确认页面进入才能访问注册页面,否则将被重定向回主页。

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


Redirecting...

Redirecting...

You should be redirected automatically to target URL: /register?emp_id=1. If not click the link.

太好了!这正是我要找的!我想被重定向到注册页面。

现在当我确实指定了 follow_redirects=True 并打印出响应数据时,我期望返回注册页面的HTML。但是响应数据却返回了主页的HTML。

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

这是我正在使用的代码:

views.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)

TESTS

base.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
< p> db_data 参数是在< strong> base.py 中的一个固定参数,它只是使用必要的数据填充数据库,以使注册流程正常工作。

我的目标是在不将确认和注册分成两个测试的情况下测试完整的注册流程。

1个回答

4
Flask测试客户端在跟随重定向时似乎没有添加“Referer”标头。
你可以实现自己的重定向版本。也许像这样:
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

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

这个方法奏效了。我遇到了一个问题,其中拼写Referer错误,无法正常重定向。我之前提到在POST中设置Referer头,但是听起来我把它放在了错误的HTTP操作上。感谢你澄清了这一点,Miguel。 - laskeym13

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接