Testing
Test your custody integration using mocking and testing best practices.
What This Section Covers
Testing patterns, mocking the Custody SDK, pytest fixtures, and best practices for testing applications that use the Custody SDK.
Testing with Mocks
Use unittest.mock or pytest-mock to mock SDK responses:
pytest Example
import pytest
from unittest.mock import MagicMock
from vecu_custody import CustodyClient
from vecu_custody.models import Authorization, AuthorizationStatus
@pytest.fixture
def mock_client(mocker):
"""Provide a mocked custody client."""
client = MagicMock(spec=CustodyClient)
# Mock authorization creation
mock_auth = Authorization(
authorization_id="AUTH-12345678",
vin="9HGBH41JXMN999999",
status=AuthorizationStatus.PENDING,
origin="LOC-AUCTION-MANHEIM-ATLANTA",
destination="LOC-DEALERSHIP-CARMAX-ORLANDO",
person_identity_key="vecu_test123",
role="DRIVER",
created_at="2026-01-23T10:30:00Z",
expires_at="2026-01-24T10:30:00Z"
)
client.authorizations.create.return_value = mock_auth
return client
def test_create_authorization(mock_client):
authorization = mock_client.authorizations.create(
vin="9HGBH41JXMN999999",
origin="LOC-AUCTION-MANHEIM-ATLANTA",
destination="LOC-DEALERSHIP-CARMAX-ORLANDO",
person_identity_key="vecu_test123",
authorized_by="system-integration",
make_model="Honda Accord 2023"
)
assert authorization.authorization_id == "AUTH-12345678"
assert authorization.vin == "9HGBH41JXMN999999"
assert authorization.status == AuthorizationStatus.PENDING
unittest Example
import unittest
from unittest.mock import MagicMock
from vecu_custody import CustodyClient
class TestCustodyIntegration(unittest.TestCase):
def setUp(self):
self.client = MagicMock(spec=CustodyClient)
def test_create_authorization(self):
mock_auth = MagicMock()
mock_auth.authorization_id = "AUTH-12345678"
mock_auth.vin = "9HGBH41JXMN999999"
self.client.authorizations.create.return_value = mock_auth
authorization = self.client.authorizations.create(
vin="9HGBH41JXMN999999",
origin="LOC-AUCTION-MANHEIM-ATLANTA",
destination="LOC-DEALERSHIP-CARMAX-ORLANDO",
person_identity_key="vecu_test123",
authorized_by="system-integration",
make_model="Honda Accord 2023"
)
self.assertEqual(authorization.authorization_id, "AUTH-12345678")
self.assertEqual(authorization.vin, "9HGBH41JXMN999999")
Testing with pytest
Fixtures
import pytest
from unittest.mock import MagicMock
from vecu_custody import CustodyClient
@pytest.fixture
def custody_client():
"""Provide a mock custody client."""
return MagicMock(spec=CustodyClient)
@pytest.fixture
def releasable_result():
"""Mock releasable result."""
result = MagicMock()
result.is_releasable = True
result.blockers = []
return result
@pytest.fixture
def blocked_result():
"""Mock blocked result."""
result = MagicMock()
result.is_releasable = False
result.blockers = ["HOLD", "PAYMENT"]
return result
def test_releasable_vehicle(custody_client, releasable_result):
custody_client.releasability.check_vin.return_value = releasable_result
result = custody_client.releasability.check_vin(vin="9HGBH41JXMN999999")
assert result.is_releasable is True
assert result.blockers == []
def test_blocked_vehicle(custody_client, blocked_result):
custody_client.releasability.check_vin.return_value = blocked_result
result = custody_client.releasability.check_vin(vin="9HGBH41JXMN999999")
assert result.is_releasable is False
assert len(result.blockers) == 2
Parametrized Tests
import pytest
from unittest.mock import MagicMock
@pytest.mark.parametrize("vin,expected_releasable", [
("9HGBH41JXMN999999", True),
("1HGCM82633A123456", True),
("BLOCKED_VIN", False),
])
def test_releasability_check(vin, expected_releasable):
client = MagicMock()
result = MagicMock()
result.is_releasable = expected_releasable
client.releasability.check_vin.return_value = result
result = client.releasability.check_vin(vin=vin)
assert result.is_releasable == expected_releasable
Testing Async Code
Async Fixtures
import pytest
from unittest.mock import AsyncMock
from vecu_custody.aio import AsyncCustodyClient
@pytest.fixture
async def async_client():
"""Provide an async mock client."""
return AsyncMock(spec=AsyncCustodyClient)
@pytest.mark.asyncio
async def test_async_create_authorization(async_client):
mock_auth = AsyncMock()
mock_auth.authorization_id = "AUTH-12345678"
mock_auth.vin = "9HGBH41JXMN999999"
async_client.authorizations.create.return_value = mock_auth
authorization = await async_client.authorizations.create(
vin="9HGBH41JXMN999999",
origin="LOC-AUCTION-MANHEIM-ATLANTA",
destination="LOC-DEALERSHIP-CARMAX-ORLANDO",
person_identity_key="vecu_test123",
authorized_by="system-integration",
make_model="Honda Accord 2023"
)
assert authorization.authorization_id == "AUTH-12345678"
assert authorization.vin == "9HGBH41JXMN999999"
@pytest.mark.asyncio
async def test_concurrent_operations(async_client):
import asyncio
mock_result = AsyncMock()
mock_result.is_releasable = True
async_client.releasability.check_vin.return_value = mock_result
vins = ["VIN1", "VIN2", "VIN3"]
tasks = [
async_client.releasability.check_vin(vin=vin)
for vin in vins
]
results = await asyncio.gather(*tasks)
assert len(results) == 3
Mocking with unittest.mock
Patching the Client
from unittest.mock import patch, MagicMock
from vecu_custody import CustodyClient
def test_with_patched_client():
with patch('vecu_custody.CustodyClient.sandbox') as mock_sandbox:
mock_client = MagicMock()
mock_auth = MagicMock()
mock_auth.authorization_id = "AUTH-123"
mock_client.authorizations.create.return_value = mock_auth
mock_sandbox.return_value = mock_client
# Your application code that uses the client
client = CustodyClient.sandbox(token="test-token")
authorization = client.authorizations.create(
vin="9HGBH41JXMN999999",
origin="LOC-AUCTION-MANHEIM-ATLANTA",
destination="LOC-DEALERSHIP-CARMAX-ORLANDO",
person_identity_key="vecu_test123",
authorized_by="system-integration",
make_model="Honda Accord 2023"
)
assert authorization.authorization_id == "AUTH-123"
Patching Specific Methods
from unittest.mock import patch, MagicMock
from vecu_custody import CustodyClient
def test_releasability_check():
client = CustodyClient.sandbox(token="test-token")
with patch.object(client.releasability, 'check_vin') as mock_check:
mock_result = MagicMock()
mock_result.is_releasable = False
mock_result.blockers = ["HOLD"]
mock_check.return_value = mock_result
result = client.releasability.check_vin(vin="9HGBH41JXMN999999")
assert result.is_releasable is False
assert len(result.blockers) == 1
Integration Testing
Test Against Sandbox
import pytest
from vecu_custody import CustodyClient
import os
@pytest.fixture
def sandbox_client():
"""Provide a real client connected to sandbox."""
if not os.getenv("VECU_API_TOKEN"):
pytest.skip("Sandbox credentials not available")
return CustodyClient.sandbox(token=os.environ["VECU_API_TOKEN"])
@pytest.mark.integration
def test_real_api_call(sandbox_client):
"""Integration test against sandbox environment."""
# This makes a real API call
health = sandbox_client.get_health()
assert health.status == "healthy"
Running Integration Tests Separately
# Run only unit tests (with mocks)
pytest -m "not integration"
# Run only integration tests (real API calls)
pytest -m integration
# Run all tests
pytest
Test Helpers
Custom Assertions
from vecu_custody.models import AuthorizationStatus
def assert_valid_authorization(authorization):
"""Assert that an authorization has all required fields."""
assert authorization.authorization_id is not None
assert authorization.vin is not None
assert authorization.status in [
AuthorizationStatus.PENDING,
AuthorizationStatus.RELEASABLE,
AuthorizationStatus.CANCELLED,
AuthorizationStatus.EXPIRED
]
assert authorization.person_identity_key is not None
def test_authorization_creation():
mock_auth = MagicMock()
mock_auth.authorization_id = "AUTH-123"
mock_auth.vin = "9HGBH41JXMN999999"
mock_auth.status = AuthorizationStatus.PENDING
mock_auth.person_identity_key = "vecu_test123"
assert_valid_authorization(mock_auth)
Test Data Builders
from dataclasses import dataclass
from typing import Optional
@dataclass
class AuthorizationBuilder:
vin: str = "9HGBH41JXMN999999"
origin: str = "LOC-AUCTION-MANHEIM-ATLANTA"
destination: str = "LOC-DEALERSHIP-CARMAX-ORLANDO"
person_identity_key: str = "vecu_test123"
authorized_by: str = "system-integration"
make_model: str = "Honda Accord 2023"
def build(self, client):
return client.authorizations.create(
vin=self.vin,
origin=self.origin,
destination=self.destination,
person_identity_key=self.person_identity_key,
authorized_by=self.authorized_by,
make_model=self.make_model
)
def test_with_builder():
client = MagicMock()
mock_auth = MagicMock()
client.authorizations.create.return_value = mock_auth
authorization = AuthorizationBuilder(vin="CUSTOM_VIN").build(client)
client.authorizations.create.assert_called_once()
args, kwargs = client.authorizations.create.call_args
assert kwargs['vin'] == "CUSTOM_VIN"
Coverage
Measuring Test Coverage
# Install coverage
pip install pytest-cov
# Run tests with coverage
pytest --cov=my_app --cov-report=html
# View coverage report
open htmlcov/index.html
Coverage Configuration
# pytest.ini or setup.cfg
[tool:pytest]
addopts = --cov=my_app --cov-report=term-missing
testpaths = tests
[coverage:run]
source = my_app
omit =
*/tests/*
*/venv/*
Best Practices
1. Use Mocks for Unit Tests
# Good - fast, isolated unit test
def test_create_authorization():
client = MagicMock(spec=CustodyClient)
mock_auth = MagicMock()
client.authorizations.create.return_value = mock_auth
authorization = client.authorizations.create(...)
assert authorization is not None
# Avoid - slow, requires API access
def test_create_authorization():
client = CustodyClient.sandbox(token="...") # Real API call
authorization = client.authorizations.create(...)
2. Test Error Paths
from vecu_custody.exceptions import ValidationError
def test_error_handling():
client = MagicMock()
client.authorizations.create.side_effect = ValidationError("Invalid VIN")
with pytest.raises(ValidationError):
client.authorizations.create(vin="INVALID", ...)
3. Separate Unit and Integration Tests
# tests/unit/test_authorizations.py
from unittest.mock import MagicMock
def test_authorization_logic():
client = MagicMock()
# Test business logic
# tests/integration/test_api.py
import pytest
from vecu_custody import CustodyClient
@pytest.mark.integration
def test_real_api():
client = CustodyClient.sandbox(token="...")
# Test against real API
4. Use Fixtures for Common Setup
@pytest.fixture
def releasable_client():
client = MagicMock()
result = MagicMock()
result.is_releasable = True
client.releasability.check_vin.return_value = result
return client
@pytest.fixture
def blocked_client():
client = MagicMock()
result = MagicMock()
result.is_releasable = False
result.blockers = ["HOLD"]
client.releasability.check_vin.return_value = result
return client
def test_releasable_flow(releasable_client):
result = releasable_client.releasability.check_vin(vin="...")
assert result.is_releasable is True
def test_blocked_flow(blocked_client):
result = blocked_client.releasability.check_vin(vin="...")
assert result.is_releasable is False
Next Steps
- Error Handling - Handle exceptions in tests
- Advanced Async Patterns - Test async code with concurrency patterns
- API Reference - Complete API documentation