This guide covers testing strategies, patterns, and workflows for the Sagemcom API.
The test suite uses pytest with async support to validate the client's behavior against mocked Sagemcom router API responses. Tests are split into:
- Unit tests (
tests/unit/) - Mock-based tests for individual methods, error handling, and encryption - Integration tests (
tests/integration/) - Tests against real or comprehensive simulated router APIs (requires device access) - Fixtures (
tests/fixtures/) - Sample API response payloads from different router models
# Run all tests
uv run pytest
# Run only unit tests
uv run pytest tests/unit/
# Run with coverage report
uv run pytest --cov=sagemcom_api
# Run with coverage HTML report
uv run pytest --cov=sagemcom_api --cov-report=html
# Run specific test file
uv run pytest tests/unit/test_client_basic.py
# Run specific test
uv run pytest tests/unit/test_client_basic.py::test_login_successWe mock at the aiohttp.ClientSession.post level to:
- Simulate realistic HTTP interactions
- Test full request/response cycle including JSON encoding/decoding
- Validate request payload structure
- Control response status codes and payloads
All fixtures are defined in tests/conftest.py with function scope for test isolation:
mock_session_factory- Factory for creating mock aiohttp sessions with custom responseslogin_success_response- Mock response for successful loginlogin_auth_error_response- Mock response for authentication failuremock_client_...- Pre-configured SagemcomClient with mocked session
Example usage:
@pytest.mark.asyncio
async def test_example(mock_session_factory, login_success_response):
mock_session = mock_session_factory([login_success_response])
client = SagemcomClient("192.168.1.1", "admin", "password",
EncryptionMethod.MD5, session=mock_session)
# Test implementation...Realistic API responses are stored in tests/fixtures/ as JSON files mirroring actual router responses:
login_success.json- Successful login with session_id and noncelogin_auth_error.json- Authentication failure (XMO_AUTHENTICATION_ERR)device_info.json- Device information responsehosts.json- Connected devices listxpath_value.json- Generic XPath query response
These fixtures preserve the actual JSON-RPC structure from Sagemcom routers:
{
"reply": {
"error": {"description": "XMO_REQUEST_NO_ERR"},
"actions": [{
"callbacks": [{
"parameters": {"id": 12345, "nonce": "abcdef123456"}
}]
}]
}
}All three encryption methods (MD5, SHA512, MD5_NONCE) must be tested. See test_client_basic.py for examples:
@pytest.mark.asyncio
async def test_login_success(mock_session_factory, login_success_response):
"""Test successful login flow."""
# Demonstrates mocking login with session_id/nonce exchangeEach XMO_*_ERR constant should have corresponding test cases:
@pytest.mark.asyncio
async def test_authentication_error(mock_session_factory, login_auth_error_response):
"""Test AuthenticationException is raised on XMO_AUTHENTICATION_ERR."""
# Demonstrates error response mockingValidate URL encoding with safe characters preserved:
@pytest.mark.asyncio
async def test_xpath_url_encoding(mock_session_factory):
"""Test XPath values are URL-encoded with /=[]' preserved."""
# Demonstrates XPath encoding validationMost API operations require multiple HTTP requests (login → operation). Use mock_session_factory with response lists:
# Two sequential responses
mock_session = mock_session_factory([login_success_response, xpath_value_response])
await client.login() # Consumes login_success_response (1st call)
await client.get_value_by_xpath() # Consumes xpath_value_response (2nd call)
await client.logout() # Would raise StopIteration (no 3rd response)- Determine what you're testing (method, error case, encryption variant)
- Create or reuse fixture for API response in
tests/fixtures/ - Create test file in
tests/unit/test_<module>.py - Use
mock_session_factoryto inject responses - Write assertions for both success and error paths
- Run test with coverage to verify new lines are covered
- Document router model and firmware version in test docstring
- Create test in
tests/integration/test_<feature>.py - Add conditional skip if router not available:
@pytest.mark.skipif(...) - Use real credentials from environment variables, not hardcoded
Run coverage reports regularly:
uv run pytest --cov=sagemcom_api --cov-report=term-missingThe --cov-report=term-missing shows which lines lack coverage.