Session key is not modified from Flask tests

僤鯓⒐⒋嵵緔 提交于 2019-12-08 05:00:49

问题


I am building a tests for my Flask application, in one of the test there is a need to modify a session key (which itself is a list of values), and then check that app behaviour is altered by the modified key content. I'm using an approach from Flask documentation for modifying session from tests.

Here is an excerpt example code, to demonstrate the problem (I have added print statements, along with what they are printing during test run):

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)

My questions are:

  1. Why is this happening?
  2. What is the proper way to modify session['list'] from tests?

回答1:


  1. As this answer, as well as doc on flask.session modified attribute mention:

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.

So, the answer to question 1 is: it happens because list is a mutable structure and thus it's modification in session is not picked up automatically.

  1. The proper way to modify mutable structure (it doesn't matter from tests or not) is to set session.modified to True after change was done.

So, revised code for test_my_app.py would look like this:

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)

Here are some cases I have found (and you might as well) interesting, which I've stumbled upon during digging around this problem:

  1. (pretty obvious one): Mutables are modified if assignment and modification happens in the same context.

So, something like this:

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

Will return ['two', 'three']

  1. Like in the original question, in the context of modifying function the modification will be done (which could be quite misleading).

Consider the following:

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

Now, running the app and accessing the following urls:

.../create/ # ['one', 'two']
.../modify/ # ['one', 'two', 'four']
.../display/ # ['one', 'two'] still
  1. Another case, that is quite confusing is when you calling render_template() at the end of your function, and appearance of the template depending on the mutable in session. In such case session is being passed into the template from current context, which is quite similar to the previous case.

Assume we have:

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>

Once we'll call:

.../create/ # ['one', 'two']
.../modify/ # will render page containing ['one', 'two', 'four']
.../display/ # though, still ['one', 'two']


来源:https://stackoverflow.com/questions/46949860/session-key-is-not-modified-from-flask-tests

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!