问题
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:
- Why is this happening?
- What is the proper way to modify
session['list']
from tests?
回答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.
- The proper way to modify mutable structure (it doesn't matter from tests or not) is to set
session.modified
toTrue
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:
- (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']
- 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
- 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 casesession
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