Skip to main content

Testing Flask Applications

Flask provides built-in tools for testing your application without needing to run a full web server. These tools allow you to simulate requests, inspect application state, and verify command-line interfaces.

Testing Mode

When you test a Flask application, you should enable the TESTING configuration flag. By default, Flask handles exceptions by returning a 500 error page. When TESTING is True, exceptions are propagated to the test client, allowing your test runner to show the actual traceback and assertion errors.

app = Flask(__name__)
app.config["TESTING"] = True

Internally, Flask.handle_exception checks this flag via self.testing. If enabled, it re-raises the exception instead of logging it and returning a generic error response.

The Test Client

The test_client() method on the Flask object returns an instance of FlaskClient (a subclass of werkzeug.test.Client). This client allows you to send requests to your application and receive the response object.

Making Requests

You can use the client to simulate various HTTP methods like GET, POST, PUT, and DELETE.

def test_home_page(app):
client = app.test_client()
response = client.get("/")
assert response.status_code == 200
assert b"Welcome" in response.data

For POST requests, you can pass data as a dictionary to the data parameter or as JSON using the json parameter:

def test_create_user(client):
# Sends application/x-www-form-urlencoded
response = client.post("/users", data={"username": "flask_user"})

# Sends application/json
response = client.post("/api/data", json={"key": "value"})

Preserving Context with the with Block

Normally, Flask's context locals like request, session, and g are cleaned up at the end of a request. If you need to inspect these variables after a request is made, use the test client as a context manager.

When used in a with block, the FlaskClient preserves the request context until the block exits. This is useful for verifying that a view correctly modified the session or set a global variable.

def test_login(client, auth):
with client:
auth.login()
# These are accessible because the context is preserved
from flask import session, g
assert session["user_id"] == 1
assert g.user["username"] == "test"

This behavior is implemented in FlaskClient.__enter__ and __exit__, which set a preserve_context flag that tells the application not to pop the context stack immediately.

Session Transactions

If you need to set up specific session data before making a request, use the session_transaction() method. This method returns a context manager that allows you to modify the session directly.

def test_session_data(client):
with client.session_transaction() as sess:
sess["user_id"] = 42
sess["is_admin"] = True

# Now make a request that depends on that session data
response = client.get("/admin")
assert response.status_code == 200

The session_transaction method ensures that the session is properly opened, modified, and then saved back to the cookie jar (or other session backend) before the next request is made.

Testing CLI Commands

Flask applications often include custom CLI commands registered via app.cli. You can test these using the test_cli_runner() method, which returns a FlaskCliRunner.

The runner's invoke method executes the command in an isolated environment and returns a Result object containing the output and exit code.

def test_init_db_command(app):
runner = app.test_cli_runner()

# Invoke the 'init-db' command
result = runner.invoke(args=["init-db"])

assert "Initialized the database" in result.output
assert result.exit_code == 0

The FlaskCliRunner automatically passes a ScriptInfo object to the command, which allows the CLI to find and load your Flask application correctly during the test.

Manual Context Management

In some scenarios, you may want to test a function that relies on an application or request context without making a full request. You can push these contexts manually using app.app_context() or app.test_request_context().

def test_utility_function(app):
with app.app_context():
# current_app and g are now available
from flask import current_app
assert current_app.name == "myapp"

with app.test_request_context("/path?arg=value"):
# request and session are now available
from flask import request
assert request.path == "/path"
assert request.args["arg"] == "value"

The test_request_context method uses EnvironBuilder internally to create a WSGI environment that mimics a real request based on the arguments you provide.