会话密钥未从 Flask 测试中修改

Session key is not modified from Flask tests

我正在为我的 Flask 应用程序构建一个测试,在其中一个测试中需要修改会话密钥(它本身是一个值列表),然后检查修改后的应用程序行为是否发生了变化关键内容。我正在使用 Flask documentation 中的方法从测试中修改 session

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

my_app.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'] 的正确方法是什么?
  1. , as well as doc on flask.session modified attribute所述:

True if the session object detected a modification. Be advised that modifications on mutable structures are not picked up automatically, in that situation you have to explicitly set the attribute to True yourself.

因此,问题 1 的答案是:发生这种情况是因为列表是一个可变结构,因此它在会话中的修改不会自动获取。

  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'])

会 return ['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 被从当前上下文传递到模板中,这与之前的情况非常相似。

假设我们有:

my_app.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']