Testing Code That Requires a Flask App or Request Context

Testing code that requires a Flask app or request context

If you want to make a request to your application, use the test_client.

c = app.test_client()
response = c.get('/test/url')
# test response

If you want to test code which uses an application context (current_app, g, url_for), push an app_context.

with app.app_context():
# test your app context code

If you want test code which uses a request context (request, session), push a test_request_context.

with current_app.test_request_context():
# test your request context code

Both app and request contexts can also be pushed manually, which is useful when using the interpreter.

>>> ctx = app.app_context()
>>> ctx.push()

Flask-Script or the new Flask cli will automatically push an app context when running the shell command.


Flask-Testing is a useful library that contains helpers for testing Flask apps.

RuntimeError: working outside of request context Flask Session

What you want is the request context, not the app context.

Flask includes some handy functions to push a request context for you - check out the Flask testing docs and you'll see a lot of relevant info, including the test_request_context method on the app object.

Combine that with app.test_client to push a request context and then simulate client behaviour such as POSTing to your endpoint. Try this:

with self.app.test_request_context('/'), self.app.test_client() as c:
rv = c.post('/')
assert session.get('username') == self.user_dict()['username']

How to always provide a context for Flask app tested with PyTest?

pytest-flask seems to configure the context on any call.

conftest.py

import pytest
from flask import Flask

@pytest.fixture
def app():
app = Flask(__name__)
return app

test_main.py

import pytest
from flask import current_app, Flask

from main import main, some_method

def test_some_method(app):
#with app.app_context():
# calling and doing some assertion
some_method()

works.

How to use application context to mock flask request

Ok, I figured it out. Just needed to use the test_request_context which I read about here: link.

Here is my new TestLaunch.py:

import unittest
from mock import patch, MagicMock
from flask import request
from Launch import app, writeDatabase

class TestLaunch(unittest.TestCase):

@patch('Launch.runController')
def test_writeDatabase(self,runController):
resp = MagicMock()
runController.return_value = resp

with app.test_request_context() as c:
ret = writeDatabase()

assert ret == resp
runController.assert_called_with(request)

if __name__ == '__main__':
unittest.main()

I am not testing the URL (which I wasn't originally anyway), but I am able to test that the flask request object is being handled properly.

Why use Flask's app.test_client() over requests.Session() for testing

After a bit of digging around, I think I found a good reason to use app.test_client() as well as some other context managers that are provided by Flask.

Test Client

app.test_client()

Enables you to access local contexts via its context manager
This is extremely helpful when trying to access variables on the _request_ctx_stack (request context stack). e.g. the request and session context locals.

Some extensions also store variables on the _request_ctx_stack. e.g. flask_login stores the current_user and flask_stateless_auth stores the current_stateless_user on the request context stack.

from flask import request
from flask_stateless_auth import current_stateless_user

with app.test_client() as c:
test_path = '/resource_for_authenticated_users'
c.get(test_path)
assert request.path == test_url
assert flask.session is not None # You can also access the session context local from here
assert current_stateless_user is not None

If this was called without the test_client() context manager, it would throw a runtime error for trying to access the request local context after the _request_ctx_stack has been popped. Also, current_stateless_user would return None

App Context

app.app_context()

Similarly, you can access variables that are stored on the app context via the app.app_context() context manager, as such:

from my_app.db import db # db connections are commonly stored on the `_app_ctx_stack`

with app.app_context():
db.create_tables()
assert db.session
assert db.query.MyTable.all()

You can also access app context global variable
(typically used to store app related global variables
that are not be used across multiple requests like the
session global)

with app.app_context():
assert g.my_global_app_var

Test Request Context

test_request_context()

Then you have the app.test_request_context() context manager which is used to activate a request context temporarily. This is best explained by the official docs.

Session Transaction Context

app.test_client().session_transaction()

Finally, there's the session_transaction() context manager. This is typically used to modify a session object. This should be nested in an app.test_client().

When the __exit__() method is called, the session is safely stored.

example from docs:

with app.test_client() as c:
with c.session_transaction() as sess:
sess['a_key'] = 'a value'

# once this is reached the session was stored

Mocking current_app in Flask unit pytest

Answering for anyone that runs into the same issue. What you can do is create a dummy Flask app and use its context in your test. No need to patch anything.

import unittest
from service import fuu

class TestService(unittest.TestCase):
app = Flask('test')

def test_generate_image_happy_path(self):
with app.app_context():
fuu()
assert 1 == 1

Of course if you want to have access to your Flask config, you'd have to pass that in to this dummy app.

How can I simulate a flask request in python to test my program?

I use this to test your issue on my local

import flask

app = flask.Flask(__name__)

@app.route("/<request>")
def main(request: flask.Request):
if flask.request.method == 'GET':
try:
request_json = request.get_json()
except:
return '', 400
else:
stuff = do_stuff(request_json)
return stuff, 200

if __name__ == "__main__":
app.run(debug=True)

then do curl

curl -i http://localhost:5000/testing

and will give output like

HTTP/1.0 400 BAD REQUEST
Content-Type: text/html; charset=utf-8
Content-Length: 0
Server: Werkzeug/2.0.1 Python/3.9.6
Date: Tue, 26 Oct 2021 16:57:19 GMT

is this expected output?



Related Topics



Leave a reply



Submit