Test Execution¶
The execution module runs property-based tests against routes using generated data and validates responses according to configuration.
Overview¶
Test execution is the final stage of the pytest-routes pipeline:
Route Discovery - Extract routes from your application
Strategy Generation - Create Hypothesis strategies for parameters
Test Creation - Build Hypothesis-powered test functions
Execution - Run tests with generated data
Validation - Check responses against expected criteria
Quick Start¶
Create and run tests programmatically:
from pytest_routes import (
RouteTestConfig,
RouteTestRunner,
get_extractor,
)
# Configure testing
config = RouteTestConfig(max_examples=50)
# Extract routes
extractor = get_extractor(app)
routes = extractor.extract_routes(app)
# Create runner and execute tests
runner = RouteTestRunner(app, config)
for route in routes:
test_func = runner.create_test(route)
try:
test_func()
print(f"PASS: {route}")
except AssertionError as e:
print(f"FAIL: {route}")
print(e)
Architecture¶
+------------------+
| RouteTestRunner |
+--------+---------+
|
| creates
v
+------------------+ +-------------------+
| Hypothesis Test |---->| RouteTestClient |
| (@given) | | (ASGI client) |
+------------------+ +-------------------+
| |
| on failure | HTTP request
v v
+------------------+ +-------------------+
| RouteTestFailure | | ASGI Application |
| (detailed error) | +-------------------+
+------------------+
API Reference¶
Test Runner¶
RouteTestRunner¶
- class pytest_routes.execution.runner.RouteTestRunner[source]¶
Bases:
objectExecutes smoke tests against routes.
- Parameters:
app (
Any)config (
RouteTestConfig)
The main test runner that orchestrates test creation and execution.
Example
from pytest_routes import RouteTestRunner, RouteTestConfig config = RouteTestConfig( max_examples=100, fail_on_5xx=True, verbose=True, ) runner = RouteTestRunner(app, config) # Create a test for a specific route test = runner.create_test(route) test() # Runs 100 Hypothesis examples # Or test all routes asynchronously import asyncio results = asyncio.run(runner.test_all_routes(routes))
- __init__(app, config)[source]¶
Initialize test runner.
- Parameters:
app (
Any) – The ASGI application.config (
RouteTestConfig) – Test configuration.
RouteTestFailure¶
- class pytest_routes.execution.runner.RouteTestFailure[source]¶
Bases:
objectDetailed information about a route test failure.
- Parameters:
Detailed information about a test failure, including the shrunk example from Hypothesis.
Attributes
route_pathThe original route pattern (e.g.,
/users/{user_id})methodHTTP method that failed
status_codeActual status code received
expected_codesList of allowed status codes
request_pathThe actual request URL with parameters filled in
path_paramsPath parameter values that caused the failure (shrunk)
query_paramsQuery parameter values that caused the failure (shrunk)
bodyRequest body that caused the failure (shrunk)
response_bodyResponse body from the server (truncated)
error_typeType of error:
"server_error_5xx","unexpected_status", or"validation_error"
Example Output
============================================================ ROUTE TEST FAILURE: GET /users/{user_id} ============================================================ Error Type: unexpected_status Request Details: Method: GET Path: /users/0 Status Code: 404 Expected: [200, 201, 204, 400, 401, 403, 422] Path Parameters (shrunk example): user_id: 0 Response Body (truncated): {"detail": "User not found"} ============================================================
- __init__(route_path, method, status_code, expected_codes, request_path, path_params=<factory>, query_params=<factory>, body=None, response_body=None, error_type='unexpected_status', request_headers=<factory>, response_headers=<factory>, auth_type=None)¶
- Parameters:
Test Client¶
RouteTestClient¶
- class pytest_routes.execution.client.RouteTestClient[source]¶
Bases:
objectAsync test client for ASGI applications.
An async ASGI test client wrapper for making requests to your application.
Example
from pytest_routes import RouteTestClient async def test_endpoint(): client = RouteTestClient(app) response = await client.request( method="GET", path="/users/123", params={"include_posts": "true"}, timeout=30.0, ) assert response.status_code == 200
- async request(method, path, *, params=None, json=None, headers=None, timeout=30.0)[source]¶
Make an HTTP request to the ASGI app.
Understanding Test Creation¶
The RouteTestRunner.create_test() method creates a Hypothesis-decorated
test function:
# What create_test() generates internally:
@settings(
max_examples=config.max_examples,
suppress_health_check=[HealthCheck.too_slow],
deadline=None,
)
@given(
path_params=generate_path_params(route.path_params, route.path),
query_params=st.fixed_dictionaries({...}),
body=generate_body(route.body_type),
)
def test_route(path_params, query_params, body):
# Format path with generated params
formatted_path = format_path(route.path, path_params)
# Make request
response = client.request(
method=route.methods[0],
path=formatted_path,
params=query_params,
json=body,
)
# Validate response
validate_response(response, route)
Async Execution¶
For async test execution, use the async methods:
import asyncio
from pytest_routes import RouteTestRunner, RouteTestConfig, get_extractor
async def run_smoke_tests():
config = RouteTestConfig(max_examples=25)
runner = RouteTestRunner(app, config)
extractor = get_extractor(app)
routes = extractor.extract_routes(app)
# Test single route
result = await runner.test_route_async(routes[0])
print(f"Route: {result['route']}, Passed: {result['passed']}")
# Test all routes
results = await runner.test_all_routes(routes)
passed = sum(1 for r in results if r["passed"])
failed = len(results) - passed
print(f"Results: {passed} passed, {failed} failed")
asyncio.run(run_smoke_tests())
Verbose Mode¶
Enable verbose output for debugging:
config = RouteTestConfig(verbose=True)
runner = RouteTestRunner(app, config)
# Output during test execution:
# -> GET /users/42
# path_params: {'user_id': 42}
# query_params: {'include_posts': True}
# <- 200
Error Handling¶
The runner distinguishes between different error types:
- Server Errors (5xx)
Caught when
fail_on_5xx=True(default). These indicate bugs in your application.- Unexpected Status Codes
When the response status is not in
allowed_status_codes. Configure this list based on your API’s expected behavior.- Validation Errors
When
validate_responses=Trueand validators fail. See Response Validation for details.
config = RouteTestConfig(
fail_on_5xx=True, # Fail on server errors
allowed_status_codes=[200, 201, 204, 400, 404, 422], # Expected codes
validate_responses=True, # Enable response validation
fail_on_validation_error=True, # Fail on validation errors
)
See Also¶
Configuration - Configuration options for test execution
Route Discovery - How routes are extracted for testing
Strategy Generation - How test data is generated
Response Validation - Response validation details