---
title: "Unit Tests"
description: "Writing unit tests for TraceRTM"
keywords: [unit tests, testing, pytest, coverage]
---

# Unit Tests

Writing unit tests for TraceRTM components.

## Test Structure

### Basic Test

```python
import pytest
from tracertm.services.item_service import ItemService

def test_create_item():
    """Test item creation."""
    service = ItemService()
    item = service.create(
        title="Test Item",
        view="requirements"
    )
    assert item["title"] == "Test Item"
    assert item["view"] == "requirements"
    assert "id" in item
```

## Test Organization

### Test Files

```
tests/
├── unit/
│   ├── test_items.py
│   ├── test_links.py
│   └── test_projects.py
├── integration/
│   └── test_api.py
└── fixtures/
    └── conftest.py
```

### Test Classes

```python
class TestItemService:
    def test_create_item(self):
        """Test creating an item."""
        pass
    
    def test_get_item(self):
        """Test getting an item."""
        pass
    
    def test_update_item(self):
        """Test updating an item."""
        pass
```

## Fixtures

### Database Fixture

```python
import pytest
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
from tracertm.database import Base

@pytest.fixture
async def db_session():
    """Create test database session."""
    engine = create_async_engine("sqlite+aiosqlite:///:memory:")
    async with engine.begin() as conn:
        await conn.run_sync(Base.metadata.create_all)
    
    async with AsyncSession(engine) as session:
        yield session
```

### Mock Fixtures

```python
from unittest.mock import Mock

@pytest.fixture
def mock_event_bus():
    """Mock event bus."""
    return Mock()

@pytest.fixture
def mock_cache():
    """Mock cache."""
    return Mock()
```

## Testing Services

### Service Tests

```python
import pytest
from tracertm.services.item_service import ItemService

def test_item_service_create(mock_repo, mock_event_bus):
    """Test item service creation."""
    service = ItemService(repo=mock_repo, event_bus=mock_event_bus)
    
    item = service.create(title="Test", view="requirements")
    
    mock_repo.create.assert_called_once()
    mock_event_bus.publish.assert_called_once_with("item.created", item)
```

## Testing Repositories

### Repository Tests

```python
import pytest
from tracertm.repositories.item_repository import ItemRepository

@pytest.mark.asyncio
async def test_repository_create(db_session):
    """Test repository create."""
    repo = ItemRepository(db_session)
    
    item = await repo.create(
        title="Test Item",
        view="requirements",
        project_id="proj_123"
    )
    
    assert item.id is not None
    assert item.title == "Test Item"
```

## Mocking

### Mock Dependencies

```python
from unittest.mock import Mock, patch

def test_service_with_mock():
    """Test service with mocked dependencies."""
    mock_repo = Mock()
    mock_repo.get_by_id.return_value = {"id": "item_123", "title": "Test"}
    
    service = ItemService(repo=mock_repo)
    item = service.get("item_123")
    
    assert item["title"] == "Test"
    mock_repo.get_by_id.assert_called_once_with("item_123")
```

## Running Tests

### Run All Tests

```bash
# Run all unit tests
pytest tests/unit/

# Run specific test file
pytest tests/unit/test_items.py

# Run specific test
pytest tests/unit/test_items.py::test_create_item
```

### With Coverage

```bash
# Run with coverage
pytest --cov=tracertm --cov-report=html

# View coverage report
open htmlcov/index.html
```

## Best Practices

### 1. Test Isolation

```python
# Good - isolated test
def test_create_item():
    item = create_item(title="Test")
    assert item["title"] == "Test"

# Bad - depends on previous test
def test_update_item():
    # Assumes item exists from previous test
    update_item(existing_item_id, ...)
```

### 2. Use Descriptive Names

```python
# Good
def test_create_item_with_valid_data():
    pass

# Bad
def test_item():
    pass
```

### 3. Test Edge Cases

```python
def test_create_item_with_empty_title():
    """Test that empty title raises error."""
    with pytest.raises(ValidationError):
        create_item(title="")

def test_create_item_with_long_title():
    """Test that long title raises error."""
    with pytest.raises(ValidationError):
        create_item(title="x" * 300)
```

## Related Documentation

- [Testing](../04-testing) - Testing overview
- [Integration Tests](./04b-integration-tests) - Integration testing
