Guide test placement in correct test pyramid layer (unit/integration/e2e). Use when creating new test files, deciding test layer, organizing test structure, or determining fixture scope...
Ensure new tests are placed in the correct test pyramid layer based on dependencies, mocking patterns, and scope. Prevents anti-patterns like unit tests with real databases or e2e tests with excessive mocking.
Use this skill when:
Trigger phrases:
Creating a new test? Ask yourself:
tests/unit/)tests/integration/)tests/e2e/)Analyze what the test needs to run:
Pattern Recognition:
# Unit test pattern - Mock objects
from unittest.mock import AsyncMock, Mock
mock_db = Mock(spec=Neo4jDatabase)
# Integration test pattern - Real fixtures
async def test_with_real_db(neo4j_database: Neo4jDatabase):
# E2E test pattern - Full system
async def test_workflow(search_handler, indexed_real_codebase):
Match fixture scope to test layer:
Unit Test Fixtures (function scope):
mock_config - Mock Settings objecttemp_dir - Temporary directorymock_neo4j_rag - Mocked Neo4jRAGmock_repository_monitor - Mocked monitorIntegration Test Fixtures (function scope, real resources):
real_settings - Settings from environmentneo4j_database - Real Neo4jDatabase instanceneo4j_driver - Real Neo4j drivertest_database - Database name with cleanupE2E Test Fixtures (session/function scope, full stack):
indexed_real_codebase - Session-level codebase indexingsearch_handler - Real SearchCodeHandlerneo4j_driver - Connected to indexed databasePlace test files following Clean Architecture layers:
tests/
āāā unit/ # Mock everything
ā āāā conftest.py # Unit test fixtures
ā āāā config/ # Domain/config tests
ā āāā application/ # Application layer tests
ā ā āāā services/
ā ā āāā commands/
ā ā āāā queries/
ā āāā infrastructure/ # Infrastructure tests (mocked)
ā āāā core/ # Core logic tests
āāā integration/ # Real infrastructure, mock external APIs
ā āāā conftest.py # Integration fixtures
ā āāā neo4j/ # Neo4j integration tests
ā āāā infrastructure/ # Real infrastructure tests
ā āāā clean_architecture/ # Cross-layer integration
āāā e2e/ # Full stack
āāā conftest.py # E2E fixtures
āāā semantic_search/ # Search E2E tests
āāā test_*.py # Workflow tests
Follow test distribution and characteristics:
Unit Tests (70% of tests):
@pytest.mark.unit markerIntegration Tests (20% of tests):
@pytest.mark.integration markerE2E Tests (10% of tests):
@pytest.mark.e2e markerCheck test placement against patterns:
Red Flags (Wrong Layer):
neo4j_database fixture ā Should be integrationGreen Flags (Correct Layer):
Mock(spec=ServiceClass)real_settings and neo4j_databasesearch_handler and indexed_real_codebaseScenario: Testing ChunkingService logic without database
# tests/unit/application/services/test_chunking_service.py
from unittest.mock import Mock
import pytest
from project_watch_mcp.application.services.chunking_service import ChunkingService
@pytest.mark.unit
async def test_chunk_size_calculation(mock_config):
"""Test chunk size calculation logic (pure function)."""
service = ChunkingService(settings=mock_config)
# Mock dependencies
content = "def foo():\n pass\n" * 100
# Test logic without external dependencies
chunks = service.calculate_chunks(content)
assert len(chunks) > 0
assert all(chunk.size <= mock_config.chunking.max_chunk_lines for chunk in chunks)
Why Unit: No database, no filesystem, tests pure logic.
Scenario: Testing Neo4jCodeRepository with real database
# tests/integration/infrastructure/neo4j/test_code_repository.py
import pytest
from project_watch_mcp.infrastructure.neo4j.code_repository import Neo4jCodeRepository
@pytest.mark.integration
async def test_store_and_retrieve_chunk(neo4j_database, real_settings):
"""Test chunk persistence in real Neo4j database."""
repository = Neo4jCodeRepository(neo4j_database.driver, real_settings)
# Create test chunk
chunk = Chunk(
chunk_hash="test_hash",
file_path="/test/file.py",
content="test content",
start_line=1,
end_line=5
)
# Test with REAL database
result = await repository.store_chunk(chunk)
assert result.success
# Verify persistence
retrieved = await repository.get_chunk("test_hash")
assert retrieved.data.content == "test content"
Why Integration: Uses real Neo4j database, tests actual persistence.
Scenario: Testing complete semantic search workflow
# tests/e2e/semantic_search/test_semantic_search_methods.py
import pytest
@pytest.mark.e2e
async def test_search_for_methods(search_handler, indexed_real_codebase):
"""Test searching for methods across real indexed codebase."""
query = SearchCodeQuery(
query_text="chunk validation",
project_name="project-watch-mcp",
search_type=SearchType.SEMANTIC,
limit=10
)
# Execute REAL search with REAL embeddings and REAL database
result = await search_handler.handle(query)
assert result.success
assert len(result.data) > 0
# Verify result quality (LLM usability test)
first_result = result.data[0]
assert "file_path" in first_result
assert "content" in first_result
assert first_result["score"] > 0.5
Why E2E: Full stack (indexed codebase, real embeddings, real Neo4j, real search).