strat-gameplay-webapp/backend/tests/unit/services/ASYNC_MOCK_PATTERN.md
Cal Corum d142c7cac9 CLAUDE: Phase 2 test infrastructure + comprehensive documentation
Added Phase 2 test infrastructure for services layer with proper async
mocking patterns and comprehensive documentation of all test coverage work.

Documentation Added:
- TEST_COVERAGE_SUMMARY.md (comprehensive 600-line coverage report)
  * Complete Phase 1 & 2 analysis
  * 53 tests documented across all files
  * Metrics, patterns, and next steps

- tests/unit/services/ASYNC_MOCK_PATTERN.md
  * Proper httpx.AsyncClient async mocking pattern
  * Helper function setup_mock_http_client()
  * Clear examples and completion guide

Tests Added (Phase 2):
- tests/unit/services/test_pd_api_client.py (16 tests)
  * Test infrastructure created
  * Async mocking helper function established
  * 5/16 tests passing (initialization + request construction)
  * Pattern fix needed for 10 remaining tests (~20 min work)

Status:
- Phase 1: 32/37 tests passing (86%) 
- Phase 2: Framework established, async pattern documented 🔄
- Total: 53 tests added, 37 passing (70%)

Impact:
- Established best practices for async HTTP client mocking
- Created reusable helper function for service tests
- Documented all coverage work comprehensively
- Clear path to completion with <30 min remaining work

Next Steps (documented in ASYNC_MOCK_PATTERN.md):
1. Apply setup_mock_http_client() to 10 remaining tests
2. Fix catcher_id in rollback tests (4 tests)
3. Add position rating service tests (future)
4. Add WebSocket ConnectionManager tests (future)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-05 12:39:32 -06:00

95 lines
3.1 KiB
Markdown

# Async Mock Pattern for httpx.AsyncClient
## Problem
Tests were failing because async context managers weren't properly mocked.
## Solution - Helper Function
A `setup_mock_http_client()` helper has been created to properly mock httpx.AsyncClient.
## Usage Pattern
### For Successful Responses
```python
@pytest.mark.asyncio
@patch('app.services.pd_api_client.httpx.AsyncClient')
async def test_get_positions(self, mock_client_class, api_client, mock_data):
"""Test fetching positions"""
# Setup mock using helper
mock_client = setup_mock_http_client(
mock_client_class,
response_data=[mock_data]
)
# Execute
ratings = await api_client.get_position_ratings(8807)
# Verify
assert len(ratings) == 1
mock_client.get.assert_called_once()
```
### For Exceptions
```python
@pytest.mark.asyncio
@patch('app.services.pd_api_client.httpx.AsyncClient')
async def test_timeout_error(self, mock_client_class, api_client):
"""Test handling timeout"""
# Setup mock to raise exception
mock_client = setup_mock_http_client(
mock_client_class,
exception=httpx.TimeoutException("Request timeout")
)
# Execute and verify exception
with pytest.raises(httpx.TimeoutException):
await api_client.get_position_ratings(8807)
```
## Remaining Work
Update these test methods to use `setup_mock_http_client()` helper:
1. `test_get_multiple_positions` - Replace mock setup with helper
2. `test_get_positions_with_filter` - Replace mock setup with helper
3. `test_get_positions_wrapped_in_positions_key` - Replace mock setup with helper
4. `test_http_404_error` - Use helper with exception parameter
5. `test_http_500_error` - Use helper with exception parameter
6. `test_timeout_error` - Use helper with exception parameter
7. `test_connection_error` - Use helper with exception parameter
8. `test_malformed_json_response` - Use helper with exception parameter
9. `test_all_fields_parsed` - Replace mock setup with helper
10. `test_optional_fields_none` - Replace mock setup with helper
## Example Replacement
### Before (Broken)
```python
@patch('httpx.AsyncClient')
async def test_example(self, mock_client_class, api_client):
mock_response = AsyncMock()
mock_response.json.return_value = [data]
mock_client = AsyncMock()
mock_client.get.return_value = mock_response
mock_client.__aenter__.return_value = mock_client
mock_client_class.return_value = mock_client
# ...
```
### After (Fixed)
```python
@patch('app.services.pd_api_client.httpx.AsyncClient')
async def test_example(self, mock_client_class, api_client):
mock_client = setup_mock_http_client(mock_client_class, response_data=[data])
# ...
```
## Key Points
1. **Patch path**: Use `'app.services.pd_api_client.httpx.AsyncClient'` not `'httpx.AsyncClient'`
2. **Context manager**: Helper properly sets up `__aenter__` and `__aexit__` as AsyncMocks
3. **Response**: Helper returns mock_client for additional assertions
4. **Exceptions**: Use `exception` parameter instead of `response_data`
## Estimated Time to Complete
~20-30 minutes to update all 10 remaining tests using find/replace pattern.