0

While putting together tests for my Flask app, I have stumbled upon behaviour I can't quite understand. In my tests I'm using approach suggested by Flask documentation for accessing and modifying session from the tests.

Say I have, really basic, project structure:

root
|-- my_app.py
|-- tests
    |-- test_my_app.py

my_app.py

from flask import (
    Flask,
    session,
    request
    )

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

@app.route('/action/', methods=['POST'])
def action():
    the_action = request.form['action']
    if session.get('test'):
        return 'Test is set, action complete.'
    else:
        return 'Oy vey!'

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()

    def testUnsetKeyViaPostNegative(self):
        with self.test_client as client:
            response = client.post('/action/')
            expected_response = 'Oy vey!'.encode('ascii')
            self.assertTrue(expected_response == response.data)

Now, if I run the test it will fail, because response returns 400 Bad Request. If the_action = request.form['action'] is commended out, everything goes smooth.

The reason I need it there is because there is a logic in app (and subsequently tests), that depends on data received (which I have omitted for brevity).

I thought changing the_action = request.form['action'] to something like the_action = request.form['action'] or '' would solve the problem, but it won't. An easy fix to this is to add some stub data to the post request, like so response = client.post('/action/', data=dict(action='stub'))

It feels to me like I'm missing some important points on how accessing&modifying session from tests work, and thus I'm not able to understand the described behaviour.

What I would like to understand is:

  1. Why simply getting data from request without adding any other logic (i.e. line the_action = request.form['action'] causing 400 Bad Request response on empty POST
  2. Why won't the_action = request.form['action'] or '' or the_action = request.form['action'] or 'stub' solve the problem, seems to me the case is as if empty string or 'stub' was sent via POST?
  • Maybe I am missing something, where do you set the data you are posting? – user3483203 Oct 21 '17 at 06:53
  • 1
    You mention that an easy fix is adding dummy data to the post request. If you are trying to pull data out of the post request you need to put data in. – user3483203 Oct 21 '17 at 07:01
  • @chris I understand that in order to pull data out I need to post it in first, what is confusing me here is that _(as it seems to me, at least)_ I'm not really pulling out anything, but just assigning the value to variable (i.e. `the_action = request.form['action']`). As it does not affect any part of `action` return logic _(it is just sitting there)_, I can not understand why `400 Bad Request` is returned. –  Oct 23 '17 at 03:35
  • 1
    "...the issue is that Flask raises an HTTP error when it fails to find a key in the args and form dictionaries. What Flask assumes by default is that if you are asking for a particular key and it's not there then something got left out of the request and the entire request is invalid." from [this](https://stackoverflow.com/questions/14105452/what-is-the-cause-of-the-bad-request-error-when-submitting-form-in-flask-applica) answer – user3483203 Oct 23 '17 at 18:54

1 Answers1

0

Based on comments by chris and on answer to the linked question, I see now that this question is, basically, a duplicate of What is the cause of the Bad Request Error when submitting form in Flask application?


To address question-points from the current question:

  1. If Flask is unable to find any keys from args or form dictionaries, it raises and HTTP error (400 Bad Request in this case). Regardless of whether or not getting the key affects logic of the app in any way (i.e. just assigning it to variable the_action = request.form['action'] will result in HTTP error, if action key does not exist in form).

This is summed up in a comment by Sean Vieira to the linked question:

Flask raises an HTTP error when it fails to find a key in the args and form dictionaries.

  1. the_action = request.form['action'] or '' or the_action = request.form['action'] or 'stub' won't suffice, because Flask will try to get a nonexistent key in request.form['action'], failing as it is not there and resulting into 400 Bad Request, before it will get to the or. With this being said - or will never be reached as if request.form['action'] has a value in it - the_action will be assigned to this value, otherwise 400 Bad Request will be returned.

To avoid this - dictionary's get() method should be used, along with passing a default value to it. So the_action = request.form['action'] or 'stub' becomes the_action = request.form.get('action', 'stub'). In this way empty POST won't result in 400 Bad Request error.