CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-bravado

Library for accessing Swagger-enabled APIs

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

testing-utilities.mddocs/

Testing Utilities

Mock objects and integration testing base classes for testing applications that use bravado. These utilities provide comprehensive testing support including response mocks, fallback result testing, and integration test fixtures for both synchronous and asynchronous HTTP clients.

Capabilities

Response Mocks

Mock objects that simulate bravado response behavior for unit testing.

IncomingResponseMock

Mock implementation of IncomingResponse for creating test responses.

class IncomingResponseMock(IncomingResponse):
    def __init__(self, status_code: int, **kwargs): ...

Parameters:

  • status_code (int): HTTP status code for the mock response
  • **kwargs: Additional response attributes (text, headers, etc.)

Usage Example:

from bravado.testing.response_mocks import IncomingResponseMock

# Create mock response
mock_response = IncomingResponseMock(
    status_code=200,
    text='{"name": "Fluffy", "status": "available"}',
    headers={'Content-Type': 'application/json'}
)

print(mock_response.status_code)  # 200
print(mock_response.text)         # {"name": "Fluffy", "status": "available"}

BravadoResponseMock

Mock that behaves like both HttpFuture.response() method and BravadoResponse object.

class BravadoResponseMock:
    def __init__(self, result, metadata=None): ...
    def __call__(self, timeout: float = None, fallback_result=None, exceptions_to_catch: tuple = None) -> 'BravadoResponseMock': ...
    @property
    def result(self): ...
    @property
    def metadata(self) -> BravadoResponseMetadata: ...

Parameters:

  • result: The mock result data to return
  • metadata: BravadoResponseMetadata instance (optional)
  • timeout (float): Timeout parameter (ignored in mock)
  • fallback_result: Fallback result (ignored unless forced)
  • exceptions_to_catch (tuple): Exception types (ignored in mock)

Usage Example:

from bravado.testing.response_mocks import BravadoResponseMock

# Create mock response
mock_result = {'name': 'Fluffy', 'status': 'available'}
mock_response = BravadoResponseMock(mock_result)

# Use as HttpFuture.response() replacement
response = mock_response()  # Call like future.response()
pet_data = response.result  # Access result like normal BravadoResponse

# Or use directly as BravadoResponse
pet_data = mock_response.result

FallbackResultBravadoResponseMock

Mock that always triggers fallback result behavior, useful for testing error handling and resilience.

class FallbackResultBravadoResponseMock:
    def __init__(self, exception=BravadoTimeoutError(), metadata=None): ...
    def __call__(self, timeout: float = None, fallback_result=None, exceptions_to_catch: tuple = None) -> 'FallbackResultBravadoResponseMock': ...
    @property
    def result(self): ...
    @property
    def metadata(self) -> BravadoResponseMetadata: ...

Parameters:

  • exception: Exception to simulate (defaults to BravadoTimeoutError)
  • metadata: BravadoResponseMetadata instance (optional)

Usage Example:

from bravado.testing.response_mocks import FallbackResultBravadoResponseMock
from bravado.exception import BravadoTimeoutError

# Mock that simulates timeout and uses fallback
fallback_mock = FallbackResultBravadoResponseMock(
    exception=BravadoTimeoutError("Request timeout")
)

# Test fallback behavior
fallback_data = {'name': 'Unknown', 'status': 'unavailable'}
response = fallback_mock(fallback_result=fallback_data)

assert response.result == fallback_data
assert response.metadata.is_fallback_result is True

Integration Testing Base Classes

Comprehensive integration testing framework for testing bravado clients with real HTTP servers.

IntegrationTestingServicesAndClient

Pytest fixtures for setting up HTTP servers and clients for integration testing.

class IntegrationTestingServicesAndClient:
    @pytest.fixture(scope='session')
    def swagger_http_server(self): ...
    
    @pytest.fixture(scope='session')  
    def not_answering_http_server(self): ...
    
    @pytest.fixture(params=['result', 'response'])
    def result_getter(self, request): ...

Fixtures:

  • swagger_http_server: Session-scoped HTTP server serving test APIs
  • not_answering_http_server: Server that doesn't respond (for timeout testing)
  • result_getter: Parameterized fixture for testing both .result() and .response() methods

IntegrationTestingFixturesMixin

Mixin class providing common fixtures and utilities for integration tests.

class IntegrationTestingFixturesMixin:
    http_client: HttpClient
    http_client_type: Type[HttpClient]
    http_future_adapter_type: Type[FutureAdapter]
    connection_errors_exceptions: Set[Exception]
    
    @classmethod
    def setup_class(cls): ...
    
    @pytest.fixture
    def swagger_client(self) -> SwaggerClient: ...
    
    def encode_expected_response(self, expected_response: dict) -> bytes: ...

Class Attributes:

  • http_client: HTTP client instance for testing
  • http_client_type: Type of HTTP client being tested
  • http_future_adapter_type: Type of future adapter being tested
  • connection_errors_exceptions: Set of connection error exception types

Methods:

  • setup_class(): Class-level setup for test configuration
  • swagger_client(): Fixture that creates SwaggerClient instances
  • encode_expected_response(): Encode expected response data

IntegrationTestsBaseClass

Base class with comprehensive integration tests that can be inherited by HTTP client implementations.

class IntegrationTestsBaseClass(IntegrationTestingFixturesMixin):
    # Multiple test methods for various scenarios:
    # - Basic API operations
    # - Error handling (4xx, 5xx status codes)
    # - Timeout behavior
    # - Connection error handling
    # - Authentication testing
    # - Request/response validation
    # - Fallback result behavior

This class provides a complete test suite that verifies HTTP client behavior across various scenarios.

Usage Patterns

Unit Testing with Mocks

import pytest
from unittest.mock import patch
from bravado.testing.response_mocks import BravadoResponseMock

class TestPetService:
    def setup_method(self):
        self.pet_service = PetService()  # Your service that uses bravado
    
    @patch('your_module.swagger_client.pet.getPetById')
    def test_get_pet_success(self, mock_get_pet):
        # Setup mock response
        expected_pet = {'id': 1, 'name': 'Fluffy', 'status': 'available'}
        mock_response = BravadoResponseMock(expected_pet)
        mock_get_pet.return_value = mock_response
        
        # Test your service
        pet = self.pet_service.get_pet(1)
        
        # Assertions
        assert pet['name'] == 'Fluffy'
        mock_get_pet.assert_called_once_with(petId=1)
    
    @patch('your_module.swagger_client.pet.getPetById')
    def test_get_pet_with_fallback(self, mock_get_pet):
        # Setup fallback mock
        from bravado.testing.response_mocks import FallbackResultBravadoResponseMock
        fallback_mock = FallbackResultBravadoResponseMock()
        mock_get_pet.return_value = fallback_mock
        
        # Test fallback behavior
        pet = self.pet_service.get_pet_with_fallback(1)
        
        assert pet['name'] == 'Unknown'  # Your fallback data
        assert pet['status'] == 'unavailable'

Integration Testing with RequestsClient

import pytest
from bravado.testing.integration_test import IntegrationTestsBaseClass
from bravado.requests_client import RequestsClient, RequestsFutureAdapter

class TestRequestsClientIntegration(IntegrationTestsBaseClass):
    """Integration tests for RequestsClient."""
    
    @classmethod
    def setup_class(cls):
        cls.http_client_type = RequestsClient
        cls.http_future_adapter_type = RequestsFutureAdapter
        cls.connection_errors_exceptions = {ConnectionError}
        super().setup_class()
    
    def test_custom_behavior(self, swagger_client):
        """Test specific to RequestsClient behavior."""
        response = swagger_client.pet.getPetById(petId=1).response()
        assert response.result['name'] == 'doggie'
        assert response.metadata.status_code == 200

Integration Testing with FidoClient

import pytest
from bravado.testing.integration_test import IntegrationTestsBaseClass
from bravado.fido_client import FidoClient, FidoFutureAdapter
import twisted.internet.error

class TestFidoClientIntegration(IntegrationTestsBaseClass):
    """Integration tests for FidoClient."""
    
    @classmethod
    def setup_class(cls):
        cls.http_client_type = FidoClient
        cls.http_future_adapter_type = FidoFutureAdapter
        cls.connection_errors_exceptions = {
            twisted.internet.error.ConnectionRefusedError,
            twisted.internet.error.DNSLookupError
        }
        super().setup_class()
    
    def test_async_behavior(self, swagger_client):
        """Test async-specific behavior."""
        future = swagger_client.pet.getPetById(petId=1)
        response = future.response()  # This integrates with Twisted
        assert response.result['name'] == 'doggie'

Custom Test Server

import bottle
import threading
import time
from bravado.testing.integration_test import _class_fqn

def create_test_server():
    """Create custom test server for your API."""
    app = bottle.Bottle()
    
    @app.route('/pets/<pet_id:int>', method='GET')
    def get_pet(pet_id):
        if pet_id == 404:
            bottle.abort(404, "Pet not found")
        return {
            'id': pet_id,
            'name': f'Pet {pet_id}',
            'status': 'available'
        }
    
    @app.route('/pets', method='POST')
    def create_pet():
        pet_data = bottle.request.json
        pet_data['id'] = 123  # Simulate ID assignment
        return pet_data
    
    return app

class TestWithCustomServer:
    @pytest.fixture(scope='class')
    def test_server(self):
        """Custom test server fixture."""
        app = create_test_server()
        
        # Start server in thread
        server_thread = threading.Thread(
            target=lambda: app.run(host='localhost', port=5000, quiet=True)
        )
        server_thread.daemon = True
        server_thread.start()
        time.sleep(0.1)  # Wait for server to start
        
        yield 'http://localhost:5000'
    
    def test_custom_api(self, test_server):
        from bravado.client import SwaggerClient
        
        # You would need to provide your own swagger spec for the custom server
        # This is just an example structure
        spec_dict = {
            'swagger': '2.0',
            'info': {'title': 'Test API', 'version': '1.0'},
            'host': 'localhost:5000',
            'paths': {
                '/pets/{petId}': {
                    'get': {
                        'operationId': 'getPetById',
                        'parameters': [
                            {'name': 'petId', 'in': 'path', 'type': 'integer', 'required': True}
                        ],
                        'responses': {'200': {'description': 'Success'}}
                    }
                }
            }
        }
        
        client = SwaggerClient.from_spec(spec_dict, origin_url=test_server)
        response = client.pet.getPetById(petId=1).response()
        assert response.result['name'] == 'Pet 1'

Testing Error Scenarios

import pytest
from bravado.testing.response_mocks import FallbackResultBravadoResponseMock
from bravado.exception import HTTPNotFound, BravadoTimeoutError

class TestErrorHandling:
    def test_http_404_handling(self):
        """Test handling of HTTP 404 errors."""
        # Mock 404 response
        from bravado.testing.response_mocks import IncomingResponseMock
        
        mock_response = IncomingResponseMock(
            status_code=404,
            text='{"error": "Pet not found"}',
            headers={'Content-Type': 'application/json'}
        )
        
        # Your error handling logic would go here
        assert mock_response.status_code == 404
    
    def test_timeout_with_fallback(self):
        """Test timeout handling with fallback results."""
        fallback_mock = FallbackResultBravadoResponseMock(
            exception=BravadoTimeoutError("Connection timeout")
        )
        
        fallback_data = {'error': 'Service temporarily unavailable'}
        response = fallback_mock(fallback_result=fallback_data)
        
        assert response.result == fallback_data
        assert response.metadata.is_fallback_result is True

Test Data Constants

The testing module provides predefined test data for integration tests:

ROUTE_1_RESPONSE: dict  # Sample API response data
ROUTE_2_RESPONSE: dict  # Alternative response data
API_RESPONSE: dict      # Complete API response structure
SWAGGER_SPEC_DICT: dict # Sample Swagger specification

These constants provide consistent test data across different test scenarios.

Best Practices

  1. Use Mocks for Unit Tests: Test your business logic without making real HTTP calls
  2. Use Integration Tests for HTTP Clients: Verify actual HTTP client behavior
  3. Test Error Scenarios: Include tests for timeouts, connection errors, and HTTP errors
  4. Test Fallback Behavior: Verify fallback results work as expected
  5. Parameterize Tests: Test both .result() and .response() methods
  6. Mock External Dependencies: Don't make real API calls in tests
# Good testing example
import pytest
from unittest.mock import patch, MagicMock
from bravado.testing.response_mocks import BravadoResponseMock, FallbackResultBravadoResponseMock

class TestPetService:
    @pytest.fixture
    def pet_service(self):
        return PetService()  # Your service class
    
    @pytest.fixture  
    def mock_client(self):
        """Mock SwaggerClient for testing."""
        client = MagicMock()
        return client
    
    def test_get_pet_success(self, pet_service, mock_client):
        """Test successful pet retrieval."""
        # Setup
        expected_pet = {'id': 1, 'name': 'Fluffy', 'status': 'available'}
        mock_response = BravadoResponseMock(expected_pet)
        mock_client.pet.getPetById.return_value = mock_response
        
        pet_service.client = mock_client
        
        # Execute
        result = pet_service.get_pet(1)
        
        # Verify
        assert result['name'] == 'Fluffy'
        mock_client.pet.getPetById.assert_called_once_with(petId=1)
    
    def test_get_pet_with_timeout_fallback(self, pet_service, mock_client):
        """Test fallback behavior on timeout."""
        # Setup fallback mock
        fallback_mock = FallbackResultBravadoResponseMock()
        mock_client.pet.getPetById.return_value = fallback_mock
        
        pet_service.client = mock_client
        
        # Execute with fallback
        result = pet_service.get_pet_with_fallback(1)
        
        # Verify fallback was used
        assert result['name'] == 'Unknown Pet'
        assert result['status'] == 'unavailable'
    
    @pytest.mark.parametrize("status_code,expected_error", [
        (404, HTTPNotFound),
        (401, HTTPUnauthorized),
        (500, HTTPInternalServerError)
    ])
    def test_get_pet_http_errors(self, pet_service, mock_client, status_code, expected_error):
        """Test various HTTP error scenarios."""
        # Setup error mock
        mock_client.pet.getPetById.side_effect = expected_error(
            response=MagicMock(status_code=status_code)
        )
        
        pet_service.client = mock_client
        
        # Execute and verify error
        with pytest.raises(expected_error):
            pet_service.get_pet(1)

Install with Tessl CLI

npx tessl i tessl/pypi-bravado

docs

authentication.md

client-management.md

configuration.md

exception-handling.md

http-clients.md

index.md

response-handling.md

spec-loading.md

testing-utilities.md

tile.json