| name | generate-test |
| description | Generates test files following TDD patterns with Given-When-Then structure. Use when creating tests for domain logic, API endpoints, or repositories. |
| allowed-tools | Read, Write, Glob, Grep |
Generate Tests
Creates test files following the project's TDD conventions.
Test Naming Convention
Mandatory format:
def test_when_<condition>_then_<expected_outcome>():
Examples:
def test_when_item_already_exists_then_raise_ItemAlreadyExistsException():
def test_when_item_does_not_exist_then_create_item():
def test_when_user_not_authenticated_then_return_401():
def test_when_valid_request_then_return_created_item():
Test Structure
Use Given-When-Then structure without comments:
def test_when_item_already_exists_then_raise_ItemAlreadyExistsException(
item_repository: IItemRepository,
):
# Given - setup
item_db = ItemDb(id=uuid.uuid4(), user_id=user_id, name="Test", description="Desc")
item_repository.create(item_db)
# When/Then - action and assertion
with pytest.raises(ItemAlreadyExistsException):
create_item(user_id, "Test", "Another desc", item_repository)
Test File Location
Mirror the source structure:
back/src/api/{module}/domain/create_item.py
back/tests/api/{module}/domain/test_create_item.py
back/src/api/{module}/infrastructure/repository.py
back/tests/api/{module}/infrastructure/test_{entity}_repository.py
Domain Test Template
import uuid
import pytest
from freezegun import freeze_time
from src.api.{module}.domain.{action} import {action_function}
from src.api.{module}.domain.exception import {Entity}NotFoundException, {Entity}AlreadyExistsException
from src.api.{module}.infrastructure.models import {Entity}Db
from src.api.{module}.infrastructure.repository import I{Entity}Repository
user_id = "test_user_123"
def test_when_{condition}_then_{outcome}(
{entity}_repository: I{Entity}Repository,
):
{entity}_db = {Entity}Db(id=uuid.uuid4(), user_id=user_id, name="Test", description="Desc")
{entity}_repository.create({entity}_db)
with pytest.raises({Entity}AlreadyExistsException):
{action_function}(user_id, "Test", "Another desc", {entity}_repository)
@freeze_time("2023-01-01 00:00:00", tz_offset=0)
def test_when_{condition}_then_{outcome}(
{entity}_repository: I{Entity}Repository,
frozen_datetime,
):
{entity}_db = {action_function}(user_id, "Test", "Description", {entity}_repository)
assert {entity}_db.user_id == user_id
assert {entity}_db.name == "Test"
assert {entity}_db.created_at == frozen_datetime
Common Fixtures
Available in back/tests/conftest.py:
@pytest.fixture
def session():
"""Database session for tests."""
@pytest.fixture
def client(session):
"""FastAPI test client."""
@pytest.fixture
def frozen_datetime():
"""Current frozen time for freezegun tests."""
@pytest.fixture
def {entity}_repository(session) -> I{Entity}Repository:
"""Repository instance for testing."""
return {Entity}Repository(session=session)
Testing Patterns
Exception Testing
def test_when_not_found_then_raise_NotFoundException(repository):
with pytest.raises(EntityNotFoundException):
get_entity(uuid.uuid4(), repository)
Datetime Testing
@freeze_time("2023-01-01 00:00:00", tz_offset=0)
def test_when_created_then_timestamps_set(repository, frozen_datetime):
entity = create_entity("name", repository)
assert entity.created_at == frozen_datetime
assert entity.updated_at == frozen_datetime
Parameterized Testing
@pytest.mark.parametrize("name,expected", [
("valid", True),
("", False),
(None, False),
])
def test_when_name_varies_then_validation_correct(name, expected):
result = validate_name(name)
assert result == expected
Running Tests
# All tests
cd back && task tests
# Specific module
uv run pytest tests/api/{module}/ -v
# Single file
uv run pytest tests/api/{module}/domain/test_create_{entity}.py -v
# Single test
uv run pytest tests/api/{module}/domain/test_create_{entity}.py::test_when_... -v