CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-google-api-python-client

Google API Client Library for Python that provides discovery-based access to hundreds of Google services with authentication, caching, and media upload/download support.

Pending
Overview
Eval results
Files

testing.mddocs/

Testing and Mocking

The Google API Python Client provides comprehensive testing utilities including HTTP mocking, response simulation, and sequence testing to enable effective unit testing without making real API calls.

Capabilities

HTTP Mocking Classes

Mock HTTP responses for testing API interactions without network calls.

class HttpMock:
    """Mock HTTP responses for testing Google API operations."""
    
    def __init__(self, filename=None, headers=None):
        """
        Initialize HTTP mock with static response.
        
        Args:
            filename (str, optional): Path to file containing mock response content
            headers (dict, optional): HTTP headers to include in mock response
        """
    
    def request(self, uri, method='GET', body=None, headers=None, 
                redirections=1, connection_type=None):
        """
        Return a mock HTTP response.
        
        Args:
            uri (str): Request URI (ignored in basic mock)
            method (str): HTTP method (ignored in basic mock)
            body (str, optional): Request body (ignored in basic mock)
            headers (dict, optional): Request headers (ignored in basic mock)
            redirections (int): Max redirections (ignored in basic mock)
            connection_type: Connection type (ignored in basic mock)
            
        Returns:
            tuple: (httplib2.Response, content) - mock response and content
        """

class HttpMockSequence:
    """Mock a sequence of HTTP responses for testing multiple requests."""
    
    def __init__(self, iterable):
        """
        Initialize mock sequence with predefined responses.
        
        Args:
            iterable: Sequence of (response, content) tuples to return in order
        """
    
    def request(self, uri, method='GET', body=None, headers=None, 
                redirections=1, connection_type=None):
        """
        Return the next mock HTTP response in the sequence.
        
        Args:
            uri (str): Request URI (logged but ignored)
            method (str): HTTP method (logged but ignored)
            body (str, optional): Request body (logged but ignored)
            headers (dict, optional): Request headers (logged but ignored)
            redirections (int): Max redirections (ignored)
            connection_type: Connection type (ignored)
            
        Returns:
            tuple: (httplib2.Response, content) - next mock response and content
            
        Raises:
            StopIteration: When all mock responses have been consumed
        """

Usage Examples

Basic HTTP Mocking

from googleapiclient import discovery
from googleapiclient.http import HttpMock
import json

# Create mock response content
mock_response = {
    'messages': [
        {'id': '1', 'threadId': 'thread1'},
        {'id': '2', 'threadId': 'thread2'}
    ]
}

# Create mock HTTP client
http_mock = HttpMock(headers={'status': '200'})
# You can also use a file: HttpMock('path/to/response.json')

# Build service with mock HTTP client
service = discovery.build('gmail', 'v1', http=http_mock)

# Make API call - returns mock data instead of real API call
result = service.users().messages().list(userId='me').execute()
print(result)  # Will print the mock_response data

File-Based Mocking

from googleapiclient.http import HttpMock
import json
import tempfile
import os

# Create temporary mock response file
mock_data = {
    'id': 'mock_message_id',
    'payload': {
        'headers': [
            {'name': 'Subject', 'value': 'Mock Email Subject'},
            {'name': 'From', 'value': 'test@example.com'}
        ]
    }
}

with tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False) as f:
    json.dump(mock_data, f)
    mock_file = f.name

try:
    # Create mock with file
    http_mock = HttpMock(filename=mock_file, headers={'status': '200'})
    service = discovery.build('gmail', 'v1', http=http_mock)
    
    # Test API call
    message = service.users().messages().get(userId='me', id='test_id').execute()
    print(f"Subject: {message['payload']['headers'][0]['value']}")
    
finally:
    # Clean up
    os.unlink(mock_file)

Sequence Mocking for Multiple Requests

from googleapiclient.http import HttpMockSequence
import httplib2
import json

# Create a sequence of mock responses
responses = [
    # First request - list messages
    (
        httplib2.Response({'status': 200}),
        json.dumps({
            'messages': [
                {'id': 'msg1', 'threadId': 'thread1'},
                {'id': 'msg2', 'threadId': 'thread2'}
            ]
        }).encode('utf-8')
    ),
    # Second request - get first message
    (
        httplib2.Response({'status': 200}),
        json.dumps({
            'id': 'msg1',
            'payload': {
                'headers': [
                    {'name': 'Subject', 'value': 'First Message'},
                    {'name': 'From', 'value': 'sender1@example.com'}
                ]
            }
        }).encode('utf-8')
    ),
    # Third request - get second message  
    (
        httplib2.Response({'status': 200}),
        json.dumps({
            'id': 'msg2', 
            'payload': {
                'headers': [
                    {'name': 'Subject', 'value': 'Second Message'},
                    {'name': 'From', 'value': 'sender2@example.com'}
                ]
            }
        }).encode('utf-8')
    )
]

# Create sequence mock
http_mock = HttpMockSequence(responses)
service = discovery.build('gmail', 'v1', http=http_mock)

# Make sequential API calls - each gets the next response
messages_list = service.users().messages().list(userId='me').execute()
print(f"Found {len(messages_list['messages'])} messages")

for msg_info in messages_list['messages']:
    message = service.users().messages().get(userId='me', id=msg_info['id']).execute()
    subject = next(h['value'] for h in message['payload']['headers'] if h['name'] == 'Subject')
    print(f"Message: {subject}")

Error Response Mocking

from googleapiclient.http import HttpMock
from googleapiclient.errors import HttpError
import httplib2
import json

# Mock an error response
error_content = {
    'error': {
        'code': 404,
        'message': 'Requested entity was not found.',
        'errors': [
            {
                'domain': 'global',
                'reason': 'notFound',
                'message': 'Requested entity was not found.'
            }
        ]
    }
}

# Create mock with error response
http_mock = HttpMock(headers={'status': '404'})
service = discovery.build('gmail', 'v1', http=http_mock)

try:
    # This will raise an HttpError with the mocked error
    message = service.users().messages().get(userId='me', id='nonexistent').execute()
except HttpError as error:
    print(f'Caught expected error: {error.status_code}')
    print(f'Error reason: {error._get_reason()}')

Unit Testing with pytest

import pytest
from googleapiclient import discovery
from googleapiclient.http import HttpMock, HttpMockSequence
from googleapiclient.errors import HttpError
import json
import httplib2

class TestGmailService:
    """Test class for Gmail service operations."""
    
    @pytest.fixture
    def mock_service(self):
        """Create a Gmail service with HTTP mocking."""
        http_mock = HttpMock(headers={'status': '200'})
        return discovery.build('gmail', 'v1', http=http_mock)
    
    def test_list_messages(self, mock_service):
        """Test listing messages with mock response."""
        # The mock will return default response
        result = mock_service.users().messages().list(userId='me').execute()
        assert 'messages' in result or result == {}  # Depends on mock setup
    
    def test_get_message_success(self):
        """Test getting a message with successful mock response."""
        mock_response = {
            'id': 'test_message_id',
            'payload': {
                'headers': [
                    {'name': 'Subject', 'value': 'Test Subject'},
                    {'name': 'From', 'value': 'test@example.com'}
                ]
            }
        }
        
        # Create mock with specific response
        http_mock = HttpMock(headers={'status': '200'})
        service = discovery.build('gmail', 'v1', http=http_mock)
        
        # Mock returns predefined response
        result = service.users().messages().get(userId='me', id='test_id').execute()
        # In real implementation, you'd set up the mock to return mock_response
    
    def test_http_error_handling(self):
        """Test HTTP error handling with mock error response."""
        # Mock 404 error
        http_mock = HttpMock(headers={'status': '404'})
        service = discovery.build('gmail', 'v1', http=http_mock)
        
        with pytest.raises(HttpError) as exc_info:
            service.users().messages().get(userId='me', id='missing').execute()
        
        assert exc_info.value.status_code == 404
    
    def test_batch_operations(self):
        """Test batch operations with sequence mocking."""
        responses = [
            (httplib2.Response({'status': 200}), b'{"id": "msg1"}'),
            (httplib2.Response({'status': 200}), b'{"id": "msg2"}'),
            (httplib2.Response({'status': 404}), b'{"error": {"code": 404}}')
        ]
        
        http_mock = HttpMockSequence(responses)
        service = discovery.build('gmail', 'v1', http=http_mock)
        
        # First two calls succeed, third fails
        result1 = service.users().messages().get(userId='me', id='msg1').execute()
        result2 = service.users().messages().get(userId='me', id='msg2').execute()
        
        with pytest.raises(HttpError):
            service.users().messages().get(userId='me', id='missing').execute()

Integration Testing Pattern

import unittest
from googleapiclient import discovery
from googleapiclient.http import HttpMockSequence
import httplib2
import json

class GmailApiTest(unittest.TestCase):
    """Integration tests for Gmail API operations."""
    
    def setUp(self):
        """Set up test fixtures."""
        self.test_messages = [
            {
                'id': 'msg1',
                'payload': {'headers': [{'name': 'Subject', 'value': 'Test 1'}]}
            },
            {
                'id': 'msg2', 
                'payload': {'headers': [{'name': 'Subject', 'value': 'Test 2'}]}
            }
        ]
    
    def test_message_workflow(self):
        """Test complete message retrieval workflow."""
        # Create sequence of responses for workflow
        responses = [
            # List messages response
            (
                httplib2.Response({'status': 200}),
                json.dumps({
                    'messages': [
                        {'id': 'msg1', 'threadId': 'thread1'},
                        {'id': 'msg2', 'threadId': 'thread2'}
                    ]
                }).encode('utf-8')
            ),
            # Get message 1
            (
                httplib2.Response({'status': 200}),
                json.dumps(self.test_messages[0]).encode('utf-8')
            ),
            # Get message 2
            (
                httplib2.Response({'status': 200}),
                json.dumps(self.test_messages[1]).encode('utf-8')
            )
        ]
        
        http_mock = HttpMockSequence(responses)
        service = discovery.build('gmail', 'v1', http=http_mock)
        
        # Execute workflow
        messages_list = service.users().messages().list(userId='me').execute()
        self.assertEqual(len(messages_list['messages']), 2)
        
        # Get each message
        for i, msg_info in enumerate(messages_list['messages']):
            message = service.users().messages().get(
                userId='me', 
                id=msg_info['id']
            ).execute()
            
            expected_subject = f'Test {i + 1}'
            actual_subject = message['payload']['headers'][0]['value']
            self.assertEqual(actual_subject, expected_subject)

if __name__ == '__main__':
    unittest.main()

Advanced Mock Patterns

from googleapiclient.http import HttpMock
import httplib2
import json

class CustomHttpMock:
    """Custom HTTP mock with request inspection and dynamic responses."""
    
    def __init__(self):
        self.requests = []  # Track all requests made
        self.responses = {}  # Map URI patterns to responses
    
    def add_response(self, uri_pattern, response_data, status=200):
        """Add a response for a specific URI pattern."""
        self.responses[uri_pattern] = (status, response_data)
    
    def request(self, uri, method='GET', body=None, headers=None, 
                redirections=1, connection_type=None):
        """Mock request with dynamic response based on URI."""
        
        # Log the request
        self.requests.append({
            'uri': uri,
            'method': method,
            'body': body,
            'headers': headers
        })
        
        # Find matching response
        for pattern, (status, data) in self.responses.items():
            if pattern in uri:
                response = httplib2.Response({'status': status})
                content = json.dumps(data).encode('utf-8') if isinstance(data, dict) else data
                return response, content
        
        # Default response
        response = httplib2.Response({'status': 200})
        return response, b'{}'
    
    def get_requests(self):
        """Get all requests that were made."""
        return self.requests.copy()

# Usage example
mock = CustomHttpMock()
mock.add_response('messages', {'messages': []})
mock.add_response('labels', {'labels': [{'id': 'INBOX', 'name': 'INBOX'}]})

service = discovery.build('gmail', 'v1', http=mock)

# Make some requests
service.users().messages().list(userId='me').execute()
service.users().labels().list(userId='me').execute()

# Inspect what requests were made
requests = mock.get_requests()
print(f"Made {len(requests)} requests:")
for req in requests:
    print(f"  {req['method']} {req['uri']}")

Install with Tessl CLI

npx tessl i tessl/pypi-google-api-python-client

docs

auth.md

channel.md

discovery.md

errors.md

http.md

index.md

media.md

mimeparse.md

model.md

schema.md

testing.md

tile.json