1

我正在为我的 Flask 应用程序构建一个测试,在其中一个测试中需要修改一个会话密钥(它本身是一个值列表),然后检查应用程序行为是否被修改后的密钥内容改变。我正在使用Flask 文档中的一种方法来修改session测试。

这是一个示例代码摘录,用于演示问题(我添加了打印语句,以及它们在测试运行期间打印的内容):

我的应用程序.py

from flask import (
Flask,
session,
)

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

@app.route('/create_list/', methods=['POST'])
def create_list():
    session['list'] = ['1', '2']
    return "List created!"

@app.route('/in_list/')
def in_list():
    print(str(session['list'])) # ['1', '2'], but why?
    if '3' in session['list']:
        return "session['list'] contains '3'!"
    else:
        return "Oy vey! '3' is not in the session['list']"

test_my_app.py

import flask

from unittest import TestCase
import my_app

class TestApp(TestCase):

def setUp(self):
    self.test_client = my_app.app.test_client()
    self.test_client.post('/create_list/')

def testAppendList(self):
    with self.test_client as client:
        with client.session_transaction() as sess:
            sess['list'].append('3')
        print(str(sess['list'])) # ['1', '2', '3'], as expected
        response = client.get('/in_list/')
        expected_response = "session['list'] contains '3'!".encode('ascii')
        self.assertTrue(expected_response == response.data)

我的问题是:

  1. 为什么会这样?
  2. session['list']从测试中修改的正确方法是什么?
4

1 回答 1

4
  1. 作为这个答案,以及关于属性提及的文档:flask.session modified

如果会话对象检测到修改,则为真。请注意,对可变结构的修改不会自动拾取,在这种情况下,您必须自己将属性显式设置为 True。

所以,问题 1 的答案是:它的发生是因为 list 是一个可变结构,因此它在 session 中的修改不会自动获取。

  1. 修改可变结构的正确方法(与测试无关)是在更改完成后设置session.modified为。True

因此,test_my_app.py的修改代码如下所示:

import flask

from unittest import TestCase
import my_app

class TestApp(TestCase):

def setUp(self):
    self.test_client = my_app.app.test_client()
    self.test_client.post('/create_list/')

def testAppendList(self):
    with self.test_client as client:
        with client.session_transaction() as sess:
            sess['list'].append('3')
            sess.modified = True
        response = client.get('/in_list/')
        expected_response = "session['list'] contains '3'!".encode('ascii')
        self.assertTrue(expected_response == response.data)

以下是我发现(您也可能)有趣的一些案例,我在挖掘这个问题时偶然发现了这些案例:

  1. (非常明显的一个):如果赋值和修改发生在同一上下文中,则修改可变变量。

所以,像这样:

@app.route('/create/')
def create():
    session['example'] = ['one', 'two']
    session['example'].append('three')
    session['example'].remove('one')
    return str(session['example'])

将返回['two', 'three']

  1. 就像在原始问题中一样,在修改功能的情况下,修改将被完成(这可能会产生很大的误导)。

考虑以下:

@app.route('/create/')
def create():
    session['example'] = ['one', 'two']
    return str(session['example'])

@app.route('/modify/')
def modify():
    session['example'].append('four')
    return str(session['example'])

@app.route('/display/')
def display():
    return str(session['example'])

现在,运行应用程序并访问以下网址:

.../create/ # ['one', 'two']
.../modify/ # ['one', 'two', 'four']
.../display/ # ['one', 'two'] still
  1. 另一种非常令人困惑的情况是,当您render_template()在函数结束时调用时,模板的外观取决于会话中的可变对象。在这种情况下session,从当前上下文传递到模板中,这与前一种情况非常相似。

假设我们有:

我的应用程序.py

@app.route('/create/')
def create():
    session['example'] = ['one', 'two']
    return str(session['example'])

@app.route('/modify/')
def modify():
    session['example'].append('four')
    return render_template('my_template.html')

@app.route('/display/')
def display():
    return str(session['example'])

my_template.html

<!doctype html>
<html>
    <head><title>Display session['example']</title></head>
    <body>
        <div>
            {% if session['example'] %}
                {{ session['example'] }}
            {% else %}
                session['example'] is not set =(
            {% endif %}
        </div>
    </body>
</html>

一旦我们调用:

.../create/ # ['one', 'two']
.../modify/ # will render page containing ['one', 'two', 'four']
.../display/ # though, still ['one', 'two']
于 2017-10-26T08:55:08.880 回答