# 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.