pytest-adapter-integration-testing
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.
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),
("KeyboaRelated in Data & Analytics
clawarr-suite
IncludedComprehensive management for self-hosted media stacks (Sonarr, Radarr, Lidarr, Readarr, Prowlarr, Bazarr, Overseerr, Plex, Tautulli, SABnzbd, Recyclarr, Unpackerr, Notifiarr, Maintainerr, Kometa, FlareSolverr). Deep library exploration, analytics, dashboard generation, content management, request handling, subtitle management, indexer control, download monitoring, quality profile sync, library cleanup automation, notification routing, collection/overlay management, and media tracker integration (Trakt, Letterboxd, Simkl).
querying-soql
IncludedSOQL query generation, optimization, and analysis with 100-point scoring. Use this skill when the user needs SOQL/SOSL authoring or optimization: natural-language-to-query generation, relationship queries, aggregates, query-plan analysis, and performance or safety improvements for Salesforce queries. TRIGGER when: user writes, optimizes, or debugs SOQL/SOSL queries, touches .soql files, or asks about relationship queries, aggregates, or query performance. DO NOT TRIGGER when: bulk data operations (use handling-sf-data), Apex DML logic (use generating-apex), or report/dashboard queries.
app-store-optimization
IncludedApp Store Optimization (ASO) toolkit for researching keywords, analyzing competitor rankings, generating metadata suggestions, and improving app visibility on Apple App Store and Google Play Store. Use when the user asks about ASO, app store rankings, app metadata, app titles and descriptions, app store listings, app visibility, or mobile app marketing on iOS or Android. Supports keyword research and scoring, competitor keyword analysis, metadata optimization, A/B test planning, launch checklists, and tracking ranking changes.
habit-flow
IncludedAI-powered atomic habit tracker with natural language logging, streak tracking, smart reminders, and coaching. Use for creating habits, logging completions naturally ("I meditated today"), viewing progress, and getting personalized coaching.
app-store-optimization
IncludedApp Store Optimization (ASO) toolkit for researching keywords, analyzing competitor rankings, generating metadata suggestions, and improving app visibility on Apple App Store and Google Play Store. Use when the user asks about ASO, app store rankings, app metadata, app titles and descriptions, app store listings, app visibility, or mobile app marketing on iOS or Android. Supports keyword research and scoring, competitor keyword analysis, metadata optimization, A/B test planning, launch checklists, and tracking ranking changes.
visualizing-data
IncludedBuilds dashboards, reports, and data-driven interfaces requiring charts, graphs, or visual analytics. Provides systematic framework for selecting appropriate visualizations based on data characteristics and analytical purpose. Includes 24+ visualization types organized by purpose (trends, comparisons, distributions, relationships, flows, hierarchies, geospatial), accessibility patterns (WCAG 2.1 AA compliance), colorblind-safe palettes, and performance optimization strategies. Use when creating visualizations, choosing chart types, displaying data graphically, or designing data interfaces.