1

环境:

  • Python 3.6.1
  • 烧瓶 0.12.2
  • Werkzeug 0.14.1

在为我的Flask应用程序编写测试时,我发现了以下特性:如果在 Flask 应用程序的测试中,您将被重定向到同一个 url 两次“连续” ClientRedirectError: loop detected将被抛出,即使它在第二次重定向后停止重定向(即循环实际上并没有发生)。

考虑以下简化示例:

应用程序.py

from flask import (
    Flask,
    redirect,
    url_for,
    session
)

app = Flask(__name__)
app.secret_key = 'improper secret key'

@app.route('/')
def index():
    if not session.get('test', False):
        session['test'] = True
        return redirect(url_for('index'))
    else:
        return "Hello"

@app.route('/redirection/')
def redirection():
    # do something — login user, for example
    return redirect(url_for('index'))

test_redirect.py

from unittest import TestCase
from app import app

class RedirectTestCase(TestCase):

def setUp(self):
    self.test_client = app.test_client()

def testLoop(self):
    response = self.test_client.get('/redirection/', follow_redirects=True)
    self.assertTrue('Hello'.encode('ascii') in response.data)

现在,如果我要运行测试——它会抛出一个ClientRedirectError: loop detected(尽管从代码中可以看出第二次重定向只会发生一次)。

如果我只是运行应用程序并转到/redirection/- 它会/毫无问题地将我带到索引(即),并且不会发生循环。

我需要if not session.get('test', False):in的原因index()是因为在我的应用程序中我使用它来设置一些东西session,以防用户/第一次访问。正如代码中的评论所建议的那样,在我的“真实”应用程序redirection()中是一个让用户登录的功能。


我的问题是:

  1. 是否有一种“正确”的方法来克服ClientRedirectError: loop detected类似情况下的投掷问题(即是否有可能使测试运行并通过)?
  2. 是否有更好/“更正确”的方式来session为“首次”用户设置内容?
  3. 是否可以将提到的行为视为werkzeug中的错误(即实际循环没有发生,但ClientRedirectError: loop detected仍然被抛出)?

解决方法,我想出了(仍然没有回答我的问题):

def testLoop(self):
    self.test_client.get('/redirection/') # removed follow_redirects=True
    response = self.test_client.get('/', follow_redirects=True) # navigating to '/', directly 
    self.assertTrue('Hello'.encode('ascii') in response.data)

这可能看起来多余,但这只是一个简化的示例(如果self.test_client.get('/redirection/')将其替换为self.test_client.post('/login/', data=dict(username='user', password='pass')).

4

1 回答 1

0

看来,我现在已经准备好(发布后几乎 1.5 个酵母)来回答我自己的问题

  1. 可以进行测试运行并通过。
    鉴于上面的代码,问题末尾提供的解决方法就可以了,尽管它可以缩短为:

    def testLoop(self):
        response = self.test_client.get('/', follow_redirects=True) # navigating to '/', directly 
        self.assertTrue('Hello'.encode('ascii') in response.data)
    

    在这种特殊情况下,不需要self.test_client.get('/redirection/'), 行。session['test']但是,如果对内部进行了一些更改/redirection/,如下所示:

    @app.route('/redirection/')
    def redirection():
        session['test'] = True
        return redirect(url_for('index'))
    

    那么 ,self.test_client.get('/redirection/')将是必要的,因此 test 将变为:

    from unittest import TestCase
    from app import app
    
    class RedirectTestCase(TestCase):
    
    def setUp(self):
        self.test_client = app.test_client()
    
    def testLoop(self):
        self.test_client.get('/redirection/')
        response = self.test_client.get('/', follow_redirects=True)
    
        self.assertTrue('Hello'.encode('ascii') in response.data)
    

    这种所谓的“解决方法”只是阻止测试/连续两次重定向到 (这正是导致的原因ClientRedirectError: loop detected- 请参阅下面第 3 点的答案)。

  2. 是否有更好的方法——将取决于应用程序实现的具体细节。在问题的示例中,内部的重定向index实际上是不必要的,可以删除:

    @app.route('/')
    def index():
        session['test'] = session.get('test', True)
        return "Hello"
    

    顺便说一句,这种变化也将使初始测试(来自问题)通过。

  3. 据我所知,这种行为是“设计使然”,即:ClientRedirectError: loop detected当重定向到相同的 url 连续发生(至少)两次时引发。下面是一些例子。

    这个,测试不会遇到ClientRedirectError(因为重定向只发生一次):

    隔离应用程序.py

    from flask import (
        Flask,
        redirect,
        url_for,
        session
        )
    
    app = Flask(__name__)
    app.secret_key = 'improper secret key'
    
    @app.route('/')
    def index():
        session['test'] = session.get('test', 0)
        if session['test'] < 1:
            session['test'] += 1
            return redirect(url_for('index'))
        else:
            return "Hello"
    

    测试.py

    from unittest import TestCase
    from isolated_app import app
    
    class RedirectTestCase(TestCase):
    
    def setUp(self):
        self.test_client = app.test_client()
    
    def testLoop(self):
        response = self.test_client.get('/', follow_redirects=True)
        self.assertTrue('Hello'.encode('ascii') in response.data)
    

    但是,如果应用程序的代码将更改为:

    from flask import (
        Flask,
        redirect,
        url_for,
        session
        )
    
    app = Flask(__name__)
    app.secret_key = 'improper secret key'
    
    @app.route('/')
    def index():
        session['test'] = session.get('test', 0)
        if session['test'] < 1:
            session['test'] += 1
            return redirect(url_for('index'))
        elif session['test'] < 2:
            session['test'] += 1
            return redirect(url_for('index'))
        else:
            return "Hello"
    

    然后测试将失败,抛出werkzeug.test.ClientRedirectError: loop detected.

    还值得一提的是,对于 Flask 测试和实际浏览,重定向循环错误的触发是不同的。如果我们运行最后一个应用程序,并/在浏览器中访问——它会很容易地返回Hello给我们。允许重定向的数量由每个浏览器的开发人员定义(例如,在 FireFox 中,它由network.http.redirection-limit来自 的首选项定义about:config,并且 20 是默认值)。这是一个插图:

    from flask import (
        Flask,
        redirect,
        url_for,
        session
        )
    
    app = Flask(__name__)
    app.secret_key = 'improper secret key'
    
    @app.route('/')
    def index():
        session['test'] = session.get('test', 0)
        while True:
            session['test'] = session.get('test', 0)
            session['test'] += 1
            print(session['test'])
            return redirect(url_for('index'))
    

    如果我们尝试对其运行测试,这将打印1 2,然后失败并显示 。werkzeug.test.ClientRedirectError: loop detected另一方面,如果我们运行这个应用程序并/在 FireFox 中访问——浏览器将向我们显示The page isn’t redirecting properly消息,并且应用程序将在控制台中打印1 2 3 4 5 6 ... 21

于 2019-04-26T06:46:09.317 回答