Python 如何避免;ClientDirectError:检测到循环";测试烧瓶应用程序时出错(以及如何触发)?

Python 如何避免;ClientDirectError:检测到循环";测试烧瓶应用程序时出错(以及如何触发)?,python,unit-testing,redirect,flask,flask-testing,Python,Unit Testing,Redirect,Flask,Flask Testing,环境: Python 3.6.1 烧瓶0.12.2 Werkzeug 0.14.1 在为我的应用程序编写测试时,我发现了以下特点:如果在Flask应用程序的测试中,您将“连续”两次重定向到同一urlClientDirectError:loop detected将被抛出,即使它在第二次重定向后停止重定向(即实际上没有发生循环) 考虑以下简化示例: app.py from flask import ( Flask, redirect, url_for, sess

环境:

  • Python 3.6.1
  • 烧瓶0.12.2
  • Werkzeug 0.14.1

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

考虑以下简化示例:

app.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'))
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)
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"
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)
测试重定向.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'))
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)
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"
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)
现在,如果我运行测试,它将抛出一个
clientredirectorror:loop detected
(尽管从代码中可以看出,第二次重定向只会发生一次)

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

我之所以需要
如果不是session.get('test',False):
index()
中的
,是因为在我的应用程序中,我使用它在
会话中设置一些东西,以防用户第一次访问
/
。正如代码中的注释所示,在我的“真实”应用程序中,
redirection()
是一个让用户登录的函数


我的问题是:

  • 是否有一种“正确”的方法来克服在类似情况下抛出
    ClientDirectError:loop detected
    (即是否有可能使测试运行并通过)
  • 是否有更好的/更正确的方法为“首次”用户设置
    会话中的内容
  • 上述行为是否可以被视为中的错误(即实际循环未发生,但仍会抛出
    clientredirector:loop detected

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

    这可能看起来多余,但这只是一个简化的示例(如果将
    self.test\u client.get('/redirection/')
    替换为类似
    self.test\u client.post('/login/',data=dict(username='user',password='pass'),可能会更有意义)
    看来,我现在准备好了(张贴后几乎有1.5个酵母)发送至:

  • 可以进行试运行并通过。
    根据上述代码,问题末尾提供的变通方法可以解决问题,但可以缩短为:

    def testLoop(self):
        response = self.test_client.get('/', follow_redirects=True) # navigating to '/', directly 
        self.assertTrue('Hello'.encode('ascii') in response.data)
    
    在这种特殊情况下,不需要使用
    self.test\u client.get('/redirection/')
    ,行。 但是,如果对
    /redirection/
    中的
    会话['test']
    进行了一些更改,如下所示:

    @app.route('/redirection/')
    def redirection():
        session['test'] = True
        return redirect(url_for('index'))
    
    然后,
    self.test\u client.get('/redirection/')
    将是必要的,所以测试将变成:

    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)
    
    这就是所谓的“变通方法”,它只是防止测试连续两次重定向到
    /
    (这正是导致
    ClientDirectError:loop detected
    ——请参阅下面第3点的答案)

  • 是否有更好的方法-将取决于应用程序实现的确切细节。在问题中的示例中,
    索引中的重定向实际上是不必要的,可以删除:

    @app.route('/')
    def index():
        session['test'] = session.get('test', True)
        return "Hello"
    
    顺便说一句,这样的改变也会使最初的测试(从问题)通过

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

    此测试不会遇到
    ClientDirectError
    (因为重定向只发生一次):

    独立应用程序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'))
    
    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)
    
    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"
    
    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"
    
    如果我们尝试运行测试,这将打印
    12
    ,然后失败
    werkzeug.test.clientDirectError:loop detected
    。另一方面,如果我们运行此应用程序并在FireFox中转到
    /
    ,浏览器将显示
    页面未正确重定向的消息,应用程序将打印
    12 3 4 5 6…21
    ,在控制台中

  • 看来,我现在已经准备好(发布后差不多有1.5个酵母)去:

  • 可以进行试运行并通过。
    根据上述代码,问题末尾提供的变通方法可以解决问题,但可以缩短为:

    def testLoop(self):
        response = self.test_client.get('/', follow_redirects=True) # navigating to '/', directly 
        self.assertTrue('Hello'.encode('ascii') in response.data)
    
    在这种特殊情况下,不需要使用
    self.test\u client.get('/redirection/')
    ,行。 但是,如果对
    /redirection/
    中的
    会话['test']
    进行了一些更改,如下所示:

    @app.route('/redirection/')
    def redirection():
        session['test'] = True
        return redirect(url_for('index'))
    
    然后,
    self.test\u client.get('/redirection/')
    将是必要的,所以测试将变成:

    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)
    
    这就是所谓的“变通方法”,它只是防止测试连续两次重定向到
    /
    (这正是导致
    ClientDirectError:loop detected
    ——请参阅下面第3点的答案)

  • 是否有更好的方法-将取决于应用程序实现的确切细节。在问题中的示例中,
    索引中的重定向实际上是不必要的,可以删除:

    @app.route('/')
    def index():
        session['test'] = session.get('test', True)
        return "Hello"
    
    顺便说一句,这样的改变也会使最初的测试(从问题)通过

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

    此测试不会遇到
    ClientDirectError
    (因为重定向只发生一次):

    独立应用程序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'))
    
    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)
    
    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"
    
    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"
    
    这将打印
    12
    ,然后以
    werkzeug.test.clientDirectError失败:检测到循环
    ,如果我们愿意的话