Response Validation

The validation module provides validators for checking HTTP responses against expected schemas, status codes, content types, and other criteria.

Overview

Response validation ensures your API responses conform to expectations:

  • Status Code Validation - Check response status codes

  • Content Type Validation - Verify Content-Type headers

  • JSON Schema Validation - Validate response bodies against schemas

  • OpenAPI Validation - Validate against OpenAPI specifications

  • Composite Validation - Combine multiple validators

Quick Start

Basic status code validation:

from pytest_routes import StatusCodeValidator, ValidationResult

validator = StatusCodeValidator(allowed_codes=[200, 201, 204])
result: ValidationResult = validator.validate(response, route)

if result.valid:
    print("Response is valid!")
else:
    print(f"Errors: {result.errors}")

Combine multiple validators:

from pytest_routes import (
    CompositeValidator,
    StatusCodeValidator,
    ContentTypeValidator,
    JsonSchemaValidator,
)

validator = CompositeValidator([
    StatusCodeValidator([200, 201]),
    ContentTypeValidator(["application/json"]),
    JsonSchemaValidator(schema={"type": "object"}),
])

result = validator.validate(response, route)

Validator Hierarchy

ResponseValidator (Protocol)
        |
        +-- StatusCodeValidator
        |
        +-- ContentTypeValidator
        |
        +-- JsonSchemaValidator
        |
        +-- OpenAPIResponseValidator
        |
        +-- CompositeValidator (combines others)

API Reference

Core Types

ValidationResult

class pytest_routes.validation.response.ValidationResult[source]

Bases: object

Result of response validation.

Variables:
  • valid – Whether the validation passed.

  • errors – List of validation error messages.

  • warnings – List of validation warning messages.

Parameters:

The result of a validation operation.

Attributes

valid

Boolean indicating if validation passed

errors

List of error messages (empty if valid)

warnings

List of warning messages (non-fatal issues)

Example

result = ValidationResult(
    valid=False,
    errors=["Status code 500 not in allowed codes"],
    warnings=["Response time exceeded 1s"],
)

if not result.valid:
    for error in result.errors:
        print(f"ERROR: {error}")
valid: bool
errors: list[str]
warnings: list[str]
__init__(valid, errors=<factory>, warnings=<factory>)
Parameters:

ResponseValidator Protocol

class pytest_routes.validation.response.ResponseValidator[source]

Bases: Protocol

Protocol for response validation.

Response validators check that HTTP responses conform to expected schemas, status codes, content types, and other criteria.

Protocol that all validators must implement.

validate(response, route)[source]

Validate a response against expected schema.

Parameters:
  • response (Any) – The HTTP response object (typically httpx.Response).

  • route (RouteInfo) – The route information for context.

Return type:

ValidationResult

Returns:

ValidationResult indicating success or failure with details.

__init__(*args, **kwargs)

Validators

StatusCodeValidator

class pytest_routes.validation.response.StatusCodeValidator[source]

Bases: object

Validate response status codes.

Checks that the response status code is within the allowed set. This is the most basic form of response validation.

Example

>>> validator = StatusCodeValidator(allowed_codes=[200, 201, 204])
>>> result = validator.validate(response, route)
>>> assert result.valid
Parameters:

allowed_codes (list[int] | None)

Validates that response status codes are within an allowed set.

Example

from pytest_routes import StatusCodeValidator

# Allow success and client error codes
validator = StatusCodeValidator(
    allowed_codes=[200, 201, 204, 400, 401, 403, 404, 422]
)

# Default: all 2xx-4xx codes (200-499)
default_validator = StatusCodeValidator()
__init__(allowed_codes=None)[source]

Initialize status code validator.

Parameters:

allowed_codes (list[int] | None) – List of allowed HTTP status codes. Defaults to all 2xx-4xx codes (200-499).

validate(response, route)[source]

Validate response status code.

Parameters:
  • response (Any) – The HTTP response object.

  • route (RouteInfo) – The route information.

Return type:

ValidationResult

Returns:

ValidationResult with status code validation.

ContentTypeValidator

class pytest_routes.validation.response.ContentTypeValidator[source]

Bases: object

Validate response content type.

Checks that the response Content-Type header matches expected types. Useful for ensuring APIs return the correct media type.

Example

>>> validator = ContentTypeValidator(expected_types=["application/json"])
>>> result = validator.validate(response, route)
>>> assert result.valid
Parameters:

expected_types (list[str] | None)

Validates the Content-Type header of responses.

Example

from pytest_routes import ContentTypeValidator

# JSON APIs
json_validator = ContentTypeValidator(
    expected_types=["application/json"]
)

# Multiple content types
multi_validator = ContentTypeValidator(
    expected_types=[
        "application/json",
        "application/xml",
        "text/html",
    ]
)
__init__(expected_types=None)[source]

Initialize content type validator.

Parameters:

expected_types (list[str] | None) – List of expected content types (e.g., [“application/json”]). Defaults to [“application/json”].

validate(response, route)[source]

Validate response content type.

Parameters:
  • response (Any) – The HTTP response object.

  • route (RouteInfo) – The route information.

Return type:

ValidationResult

Returns:

ValidationResult with content type validation.

JsonSchemaValidator

class pytest_routes.validation.response.JsonSchemaValidator[source]

Bases: object

Validate response body against JSON schema.

Uses jsonschema library (if available) to validate response bodies against a provided JSON Schema. Falls back to basic JSON validation if jsonschema is not installed.

Example

>>> schema = {"type": "object", "properties": {"id": {"type": "integer"}}}
>>> validator = JsonSchemaValidator(schema=schema)
>>> result = validator.validate(response, route)
>>> assert result.valid
Parameters:

Validates response bodies against a JSON Schema.

Note

Requires the jsonschema package for full schema validation. Install with: pip install jsonschema

Example

from pytest_routes import JsonSchemaValidator

schema = {
    "type": "object",
    "required": ["id", "name"],
    "properties": {
        "id": {"type": "integer"},
        "name": {"type": "string", "minLength": 1},
        "email": {"type": "string", "format": "email"},
    },
}

validator = JsonSchemaValidator(schema=schema, strict=True)
result = validator.validate(response, route)

if not result.valid:
    print(f"Schema errors: {result.errors}")
__init__(schema=None, *, strict=False)[source]

Initialize JSON schema validator.

Parameters:
  • schema (dict[str, Any] | None) – JSON Schema dictionary. If None, only validates JSON parsing.

  • strict (bool) – If True, require jsonschema library. If False, degrade gracefully.

validate(response, route)[source]

Validate response body against JSON schema.

Parameters:
  • response (Any) – The HTTP response object.

  • route (RouteInfo) – The route information.

Return type:

ValidationResult

Returns:

ValidationResult with JSON schema validation.

OpenAPIResponseValidator

class pytest_routes.validation.response.OpenAPIResponseValidator[source]

Bases: object

Validate response against OpenAPI schema.

Extracts expected response schemas from an OpenAPI specification and validates responses against them. This is the most comprehensive form of validation for OpenAPI-based APIs.

Example

>>> validator = OpenAPIResponseValidator(openapi_schema=openapi_spec)
>>> result = validator.validate(response, route)
>>> assert result.valid
Parameters:

openapi_schema (dict[str, Any])

Validates responses against an OpenAPI specification.

This validator automatically:

  • Finds the matching path in the OpenAPI spec

  • Looks up the expected response schema for the status code

  • Validates the response body against that schema

Example

from pytest_routes import OpenAPIResponseValidator

openapi_spec = {
    "openapi": "3.0.0",
    "paths": {
        "/users/{user_id}": {
            "get": {
                "responses": {
                    "200": {
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "id": {"type": "integer"},
                                        "name": {"type": "string"},
                                    },
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

validator = OpenAPIResponseValidator(openapi_schema=openapi_spec)
result = validator.validate(response, route)
__init__(openapi_schema)[source]

Initialize OpenAPI response validator.

Parameters:

openapi_schema (dict[str, Any]) – Full OpenAPI specification dictionary.

validate(response, route)[source]

Validate response against OpenAPI schema.

Parameters:
  • response (Any) – The HTTP response object.

  • route (RouteInfo) – The route information.

Return type:

ValidationResult

Returns:

ValidationResult with OpenAPI validation.

CompositeValidator

class pytest_routes.validation.response.CompositeValidator[source]

Bases: object

Run multiple validators in sequence.

Combines multiple validators into a single validator that runs all of them and aggregates their results. Useful for applying multiple validation rules to a single response.

Example

>>> validators = [
...     StatusCodeValidator([200, 201]),
...     ContentTypeValidator(["application/json"]),
...     JsonSchemaValidator(schema=my_schema),
... ]
>>> composite = CompositeValidator(validators)
>>> result = composite.validate(response, route)
>>> assert result.valid
Parameters:

validators (list[ResponseValidator])

Combines multiple validators into one.

All validators are run in sequence, and their results are aggregated. The composite is valid only if all child validators pass.

Example

from pytest_routes import (
    CompositeValidator,
    StatusCodeValidator,
    ContentTypeValidator,
    JsonSchemaValidator,
)

# Build a comprehensive validator
validator = CompositeValidator([
    StatusCodeValidator([200, 201]),
    ContentTypeValidator(["application/json"]),
    JsonSchemaValidator(schema={
        "type": "object",
        "required": ["data"],
    }),
])

result = validator.validate(response, route)

# Result contains all errors from all validators
if not result.valid:
    for error in result.errors:
        print(f"Validation error: {error}")

# Warnings are also aggregated
for warning in result.warnings:
    print(f"Warning: {warning}")
__init__(validators)[source]

Initialize composite validator.

Parameters:

validators (list[ResponseValidator]) – List of validators to run.

validate(response, route)[source]

Run all validators and aggregate results.

Parameters:
  • response (Any) – The HTTP response object.

  • route (RouteInfo) – The route information.

Return type:

ValidationResult

Returns:

ValidationResult with aggregated errors and warnings.

Enabling Validation

Enable response validation in your configuration:

from pytest_routes import RouteTestConfig

config = RouteTestConfig(
    validate_responses=True,
    response_validators=["status_code", "content_type"],
    fail_on_validation_error=True,
)

Available validator names:

  • "status_code" - StatusCodeValidator

  • "content_type" - ContentTypeValidator

Custom Validators

Create custom validators by implementing the ResponseValidator protocol:

from typing import Any
from pytest_routes import ValidationResult
from pytest_routes.discovery.base import RouteInfo

class ResponseTimeValidator:
    """Validate that responses are fast enough."""

    def __init__(self, max_seconds: float = 1.0):
        self.max_seconds = max_seconds

    def validate(self, response: Any, route: RouteInfo) -> ValidationResult:
        # Access response time if available
        elapsed = getattr(response, "elapsed", None)

        if elapsed is None:
            return ValidationResult(
                valid=True,
                warnings=["Response time not available"]
            )

        if elapsed.total_seconds() > self.max_seconds:
            return ValidationResult(
                valid=False,
                errors=[
                    f"Response took {elapsed.total_seconds():.2f}s, "
                    f"max allowed is {self.max_seconds}s"
                ]
            )

        return ValidationResult(valid=True)


class DeprecationHeaderValidator:
    """Check for deprecation warnings in headers."""

    def validate(self, response: Any, route: RouteInfo) -> ValidationResult:
        warnings = []

        if "Deprecation" in response.headers:
            warnings.append(
                f"Route {route.path} is deprecated: "
                f"{response.headers['Deprecation']}"
            )

        if "Sunset" in response.headers:
            warnings.append(
                f"Route {route.path} will be removed: "
                f"{response.headers['Sunset']}"
            )

        return ValidationResult(valid=True, warnings=warnings)

Use custom validators with CompositeValidator:

from pytest_routes import CompositeValidator, StatusCodeValidator

validator = CompositeValidator([
    StatusCodeValidator([200, 201]),
    ResponseTimeValidator(max_seconds=2.0),
    DeprecationHeaderValidator(),
])

See Also