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 thesession
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
Purpose of "%Matplotlib Inline"
How to Install Psycopg2 with "Pip" on Python
Making Python Loggers Output All Messages to Stdout in Addition to Log File
How to Find the Location of Python Module Sources
Seaborn Is Not Plotting Within Defined Subplots
Not Letting the Character Move Out of the Window
How to Get Indices of a Sorted Array in Python
How to Write a Multidimensional Array to a Text File
List to Dictionary Conversion with Multiple Values Per Key
How to Make a Tkinter Window Jump to the Front
List Comprehension VS Generator Expression's Weird Timeit Results
Numpy "Where" with Multiple Conditions
Pandas: Converting to Numeric, Creating Nans When Necessary