Claude
Skills
Sign in
Back

pytest-adapter-integration-testing

Included with Lifetime
$97 forever

Testing adapters and gateways: when to use unit test mocks vs testcontainers, mocking HTTP responses (aioresponses, responses), mocking Kafka messages, mocking ClickHouse responses, testing error paths and edge cases. Use when: Testing gateways and adapters, testing external service integrations, deciding between mocks and real containers, testing error handling in adapters, integration testing with real services.

Data & Analytics

What this skill does


# Pytest Adapter Integration Testing

## Purpose

Adapters connect your application to external systems. This skill covers testing both unit-level (mocked) and integration-level (real services) scenarios.


## When to Use This Skill

Use when testing adapters and gateways with "test Shopify gateway", "mock HTTP responses", "test Kafka adapter", or "decide between mocks and containers".

Do NOT use for domain testing (use `pytest-domain-model-testing`), application layer (use `pytest-application-layer-testing`), or general mocking patterns (use `pytest-mocking-strategy`).
## Quick Start

Choose testing strategy based on scope:

```python
# UNIT TEST: Mock HTTP responses
import pytest
from aioresponses import aioresponses

async def test_shopify_gateway_unit() -> None:
    """Unit test with mocked HTTP."""
    with aioresponses() as mock_http:
        mock_http.get(
            "https://test.myshopify.com/api/orders",
            payload={"orders": [{"id": "123", "total": 100}]},
        )

        gateway = ShopifyGateway(url="https://test.myshopify.com")
        orders = await gateway.fetch_orders()

        assert len(orders) == 1


# INTEGRATION TEST: Real container
from testcontainers.kafka import KafkaContainer
import pytest

@pytest.fixture(scope="module")
def kafka_container():
    """Real Kafka for integration testing."""
    with KafkaContainer() as kafka:
        yield kafka

async def test_kafka_producer_integration(kafka_container):
    """Integration test with real Kafka."""
    producer = KafkaProducerAdapter(kafka_container)
    await producer.publish("test_topic", {"data": "value"})
    # Verify message was actually published
```

## Instructions

### Step 1: Unit Test with Mocked HTTP Responses

```python
from __future__ import annotations

from aioresponses import aioresponses
from unittest.mock import AsyncMock
import pytest

from app.extraction.adapters.shopify import ShopifyGateway


class TestShopifyGatewayUnit:
    """Unit tests with mocked HTTP responses."""

    async def test_fetch_orders_success(self) -> None:
        """Test successful order fetching."""
        # Mock Shopify API response
        shopify_response = {
            "orders": [
                {
                    "id": "12345",
                    "created_at": "2024-01-01T10:00:00Z",
                    "customer": {"name": "John Doe"},
                    "line_items": [
                        {
                            "product_id": "prod_1",
                            "title": "Laptop",
                            "quantity": 1,
                            "price": "999.99",
                        }
                    ],
                    "total_price": "999.99",
                }
            ]
        }

        with aioresponses() as mock_http:
            mock_http.get(
                "https://test.myshopify.com/admin/api/2024-01/orders.json",
                payload=shopify_response,
            )

            gateway = ShopifyGateway(
                shop_url="https://test.myshopify.com",
                access_token="test_token",
            )

            # Act
            orders = []
            async for order in await gateway.fetch_orders():
                orders.append(order)

            # Assert
            assert len(orders) == 1
            assert orders[0].customer_name == "John Doe"

    async def test_fetch_orders_with_pagination(self) -> None:
        """Test pagination handling."""
        # First page response
        page1 = {
            "orders": [{"id": "1", ...}],
            "next_page_url": "https://test.myshopify.com/api/orders?page=2",
        }

        # Second page response
        page2 = {
            "orders": [{"id": "2", ...}],
        }

        with aioresponses() as mock_http:
            mock_http.get(
                "https://test.myshopify.com/api/orders",
                payload=page1,
            )
            mock_http.get(
                "https://test.myshopify.com/api/orders?page=2",
                payload=page2,
            )

            gateway = ShopifyGateway(...)

            orders = []
            async for order in await gateway.fetch_orders():
                orders.append(order)

            assert len(orders) == 2

    async def test_fetch_orders_handles_api_errors(self) -> None:
        """Test error handling for API failures."""
        from app.extraction.adapters.shopify import ShopifyApiException

        with aioresponses() as mock_http:
            # First request fails, second succeeds (retry)
            mock_http.get(
                "https://test.myshopify.com/api/orders",
                status=500,
            )
            mock_http.get(
                "https://test.myshopify.com/api/orders",
                payload={"orders": []},
            )

            gateway = ShopifyGateway(...)

            # Should succeed after retry
            orders = []
            async for order in await gateway.fetch_orders():
                orders.append(order)

            assert len(orders) == 0  # Empty but no exception

    async def test_rate_limiting_handling(self) -> None:
        """Test rate limit handling."""
        with aioresponses() as mock_http:
            # Rate limited response
            mock_http.get(
                "https://test.myshopify.com/api/orders",
                status=429,
                headers={"Retry-After": "1"},
            )

            gateway = ShopifyGateway(...)

            # Gateway should handle 429 gracefully
            # Implementation depends on retry strategy
```

### Step 2: Mock Kafka Messages

```python
from __future__ import annotations

from unittest.mock import AsyncMock, MagicMock
import pytest


@pytest.fixture
def mock_kafka_message():
    """Factory for creating mock Kafka messages."""
    def _create(key: str, value: dict) -> MagicMock:
        message = MagicMock()
        message.key.return_value = key.encode()
        message.value.return_value = json.dumps(value).encode()
        message.error.return_value = None
        return message

    return _create


@pytest.fixture
def mock_kafka_consumer(mock_kafka_message):
    """Mock Kafka consumer for unit tests."""
    mock = AsyncMock()

    # Return fake messages
    messages = [
        mock_kafka_message("order_1", {"id": "1", "total": 100}),
        mock_kafka_message("order_2", {"id": "2", "total": 200}),
    ]

    mock.consume.return_value = messages
    mock.close.return_value = None

    return mock


class TestKafkaConsumerAdapter:
    """Test Kafka consumer adapter with mocks."""

    async def test_consume_orders(self, mock_kafka_consumer) -> None:
        """Test consuming orders from Kafka."""
        adapter = KafkaConsumerAdapter(mock_kafka_consumer)

        messages = []
        async for msg in adapter.consume():
            messages.append(msg)

        assert len(messages) == 2
        assert messages[0]["id"] == "1"

    async def test_handle_deserialization_error(self, mock_kafka_consumer) -> None:
        """Test handling malformed messages."""
        # Mock message with invalid JSON
        bad_message = MagicMock()
        bad_message.value.return_value = b"invalid json"

        mock_kafka_consumer.consume.return_value = [bad_message]

        adapter = KafkaConsumerAdapter(mock_kafka_consumer)

        # Should either skip or raise, depending on implementation
        messages = []
        async for msg in adapter.consume():
            messages.append(msg)

        # Verify error was logged/handled
```

### Step 3: Mock ClickHouse Client Responses

```python
from __future__ import annotations

from unittest.mock import MagicMock
import pytest


@pytest.fixture
def mock_clickhouse_client():
    """Mock ClickHouse client."""
    from clickhouse_driver import Client

    mock = MagicMock(spec=Client)

    # Mock successful query execution
    mock.execute.return_value = [
        ("Laptop", 100),
        ("Mouse", 50),
        ("Keyboa

Related in Data & Analytics