Setting Up for Testing
To verify application behavior without a live server, you can use the built-in testing tools to simulate requests, manage sessions, and execute CLI commands in a controlled environment.
Simulating Requests with the Test Client
The most common way to test an application is using the test_client. By using the client as a context manager, you can preserve the request and application contexts to inspect globals like request, g, and session after the request has finished.
from flask import Flask, request
app = Flask(__name__)
app.testing = True # Essential for exception propagation
@app.route("/")
def index():
return f"Hello, {request.args.get('name', 'World')}"
client = app.test_client()
# Use 'with' to preserve context for assertions
with client:
response = client.get("/?name=Flask")
assert response.status_code == 200
assert response.data == b"Hello, Flask"
# Access request globals after the request is done
assert request.args["name"] == "Flask"
Key Classes and Methods
Flask.test_client(): Creates an instance ofFlaskClient(fromsrc.flask.testing) configured for your application.FlaskClient: A subclass of Werkzeug'sClientthat handles Flask-specific context preservation and session management.app.testing = True: This configuration flag ensures that exceptions are propagated to the test client rather than being caught by the app's internal error handlers (which would return a 500 response).
Manipulating Sessions
To test behavior that depends on session data, use the session_transaction() method. This allows you to modify the session before a request is made. This requires use_cookies=True (the default) on the test client.
from flask import session
with client.session_transaction() as sess:
sess["user_id"] = 42
sess["is_admin"] = True
# The next request will include these session values
response = client.get("/admin")
Testing Logic with Request Contexts
If you need to test a function that depends on the request context (like a helper using url_for or request.args) without simulating a full HTTP dispatch, use test_request_context.
from flask import url_for
def test_url_generation():
# Simulate a request to a specific path and host
with app.test_request_context("/contact", base_url="https://example.com"):
# url_for now has the context it needs to build URLs
assert url_for("index", _external=True) == "https://example.com/"
The test_request_context method accepts the same arguments as the EnvironBuilder in src/flask/testing.py, allowing you to customize headers, methods, and environment variables.
Testing CLI Commands
To test custom commands registered with app.cli, use the test_cli_runner. It automatically provides the application context during command execution.
import click
@app.cli.command("hello")
@click.option("--name", default="World")
def hello_command(name):
click.echo(f"Hello, {name}!")
runner = app.test_cli_runner()
result = runner.invoke(args=["hello", "--name", "Tester"])
assert "Hello, Tester!" in result.output
assert result.exit_code == 0
Troubleshooting and Gotchas
Exception Propagation
If your tests are receiving 500 errors instead of the actual Python exceptions you expect to see, ensure app.testing = True is set. When TESTING is False, Flask's handle_exception method (in src/flask/app.py) will log the error and return a standard 500 response.
Nested Client Invocations
The FlaskClient does not support nested with blocks. Attempting to enter a client context while already inside one will raise a RuntimeError.
# This will raise: RuntimeError: Cannot nest client invocations
with client:
with client:
client.get("/")
Accessing Globals Outside the Context
If you call client.get("/") without the with client: block, the request context is popped immediately after the response is returned. Any attempt to access flask.request or flask.session after that call will result in a RuntimeError because no context is active.