-
-
Notifications
You must be signed in to change notification settings - Fork 0
Open
Labels
Stalearea: docsDocumentationDocumentationarea: dxDeveloper experienceDeveloper experiencearea: testingTest infrastructureTest infrastructurepriority: lowLow priorityLow prioritytype: docsDocumentation improvementsDocumentation improvements
Description
Summary
Document the new testing utilities and update the testing guide to showcase how to use them effectively.
Problem Statement
Testing utilities exist but developers need:
- Understanding of when to use each utility
- Examples of extending base classes
- Patterns for common testing scenarios
Acceptance Criteria
-
docs/TESTING_GUIDE.mdupdated with fake helpers section - API reference for all
dioxide.testingutilities - Example: Custom repository fake with additional query methods
- Example: Testing time-dependent logic with FakeClock
- Example: Verifying event publication with FakeEventBus
- Example: Testing external API integration with FakeHttpClient
- All code examples tested
Documentation Content
Testing Guide Updates
## Built-in Fake Helpers
dioxide provides base classes for common fake implementations in `dioxide.testing`.
### InMemoryRepository
Generic base class for fake repositories:
\`\`\`python
from dioxide.testing import InMemoryRepository
from dioxide import adapter, Profile
@adapter.for_(UserRepository, profile=Profile.TEST)
class FakeUserRepository(InMemoryRepository[User, int]):
def get_id(self, user: User) -> int:
return user.id
def set_id(self, user: User, id: int) -> User:
user.id = id
return user
# Add custom query methods as needed
def find_by_email(self, email: str) -> User | None:
matches = self.find_by(lambda u: u.email == email)
return matches[0] if matches else None
# Usage in tests
async def test_user_registration():
repo = container.resolve(UserRepository)
repo.seed(User(id=1, name="Existing", email="exists@example.com"))
service = container.resolve(UserService)
# Should fail - email already exists
with pytest.raises(DuplicateEmailError):
await service.register("exists@example.com", "New User")
\`\`\`
### FakeClock
Controllable time for testing time-dependent logic:
\`\`\`python
from dioxide.testing import FakeClock
from dioxide import adapter, Profile
@adapter.for_(Clock, profile=Profile.TEST)
class TestClock(FakeClock):
pass
# Usage in tests
async def test_subscription_expiry():
clock = container.resolve(Clock)
clock.set_time(datetime(2024, 1, 1))
service = container.resolve(SubscriptionService)
sub = await service.create_subscription(user_id=1, days=30)
# Still valid
assert await service.is_active(sub.id) is True
# Advance past expiry
clock.advance(days=31)
# Now expired
assert await service.is_active(sub.id) is False
\`\`\`
### FakeEventBus
Capture and verify published events:
\`\`\`python
from dioxide.testing import FakeEventBus
from dioxide import adapter, Profile
@adapter.for_(EventBus, profile=Profile.TEST)
class TestEventBus(FakeEventBus[DomainEvent]):
pass
# Usage in tests
async def test_user_created_event_published():
bus = container.resolve(EventBus)
service = container.resolve(UserService)
await service.register("alice@example.com", "Alice")
# Verify event was published
assert bus.has_event(UserCreated)
# Check event details
event = bus.events_of_type(UserCreated)[0]
assert event.email == "alice@example.com"
\`\`\`
### FakeHttpClient
Record and stub HTTP requests:
\`\`\`python
from dioxide.testing import FakeHttpClient
from dioxide import adapter, Profile
@adapter.for_(HttpClient, profile=Profile.TEST)
class TestHttpClient(FakeHttpClient):
pass
# Usage in tests
async def test_fetches_user_from_api():
client = container.resolve(HttpClient)
client.stub_response(
"GET",
"https://api.example.com/users/123",
{"id": 123, "name": "Alice"},
)
service = container.resolve(ExternalUserService)
user = await service.fetch_user(123)
assert user.name == "Alice"
assert client.was_called("GET", "https://api.example.com/users/123")
\`\`\`
## When to Use Built-in Fakes vs Custom
| Scenario | Use |
|----------|-----|
| Standard CRUD repository | `InMemoryRepository` |
| Time-dependent logic | `FakeClock` |
| Event verification | `FakeEventBus` |
| External HTTP APIs | `FakeHttpClient` |
| Complex domain logic | Custom fake |
| Stateful protocols | Custom fake |Dependencies
- Blocked by: Fake Helpers: Implement Testing Utilities Package #353 (Fake Helpers Implementation)
- Blocks: None (final story in fake helpers track)
Parent Epic
Part of #336 (Production-Ready Developer Experience Improvements)
Definition of Ready
- Implementation story completed
- Acceptance criteria are testable
- Documentation outline complete
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
Stalearea: docsDocumentationDocumentationarea: dxDeveloper experienceDeveloper experiencearea: testingTest infrastructureTest infrastructurepriority: lowLow priorityLow prioritytype: docsDocumentation improvementsDocumentation improvements