没有从Flask测试修改会话密钥

时间:2017-10-26 08:55:09

标签: python flask flask-testing flask-session

我正在为我的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 个答案:

答案 0 :(得分:2)

  1. 作为this answer,以及doc on flask.session modified attribute提及:
  2.   

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

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

    1. 修改可变结构的正确方法(无论是否与测试无关)是在更改完成后将session.modified设置为True
    2. 因此, 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. (非常明显):如果在同一个上下文中进行赋值和修改,则会修改Mutable。
      2. 所以,像这样:

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

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

        1. 与原始问题一样,在修改功能的情况下,修改将会完成(这可能会产生误导)。
        2. 请考虑以下事项:

          @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将从当前上下文传递到模板中,这与前一种情况非常相似。
          2. 假设我们有:

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