Added documentation for API, architecture, configuration, database, development guide, testing, and navigation. Includes helper scripts, diagrams, and guides for NFO files and migration.
4.3 KiB
4.3 KiB
Testing Documentation
Document Purpose
This document describes the testing strategy, guidelines, and practices for the Aniworld project.
What This Document Contains
- Testing Strategy: Overall approach to quality assurance
- Test Categories: Unit, integration, API, performance, security tests
- Test Structure: Organization of test files and directories
- Writing Tests: Guidelines for writing effective tests
- Fixtures and Mocking: Shared test utilities and mock patterns
- Running Tests: Commands and configurations
- Coverage Requirements: Minimum coverage thresholds
- CI/CD Integration: How tests run in automation
- Test Data Management: Managing test fixtures and data
- Best Practices: Do's and don'ts for testing
What This Document Does NOT Contain
- Production deployment (see DEPLOYMENT.md)
- Security audit procedures (see SECURITY.md)
- Bug tracking and issue management
- Performance benchmarking results
Target Audience
- Developers writing tests
- QA Engineers
- CI/CD Engineers
- Code reviewers
Sections to Document
- Testing Philosophy
- Test pyramid approach
- Quality gates
- Test Categories
- Unit Tests (
tests/unit/) - Integration Tests (
tests/integration/) - API Tests (
tests/api/) - Frontend Tests (
tests/frontend/) - Performance Tests (
tests/performance/) - Security Tests (
tests/security/)
- Unit Tests (
- Test Structure and Naming
- File naming conventions
- Test function naming
- Test class organization
- Running Tests
- pytest commands
- Running specific tests
- Verbose output
- Coverage reports
- Fixtures and Conftest
- Shared fixtures
- Database fixtures
- Mock services
- Mocking Guidelines
- What to mock
- Mock patterns
- External service mocks
Mocking the Download Queue
Use MockQueueRepository for testing download queue functionality:
from src.server.models.download import DownloadItem, EpisodeIdentifier
class MockQueueRepository:
def __init__(self):
self._items: Dict[str, DownloadItem] = {}
Testing SetupService
SetupService handles series key resolution from folder names during library setup. Test file: tests/unit/test_setup_service.py.
Key methods tested:
_extract_year_from_folder_name()— parses(YYYY)suffix_extract_title_from_folder_name()— strips year suffix_resolve_key_via_search()— resolves provider key via fuzzy title matching
@pytest.mark.asyncio
async def test_returns_key_when_single_exact_match(self):
"""Search returns 1 result with same name → returns key."""
mock_series_app = AsyncMock()
mock_series_app.search.return_value = [
{'title': 'Attack on Titan', 'link': '/anime/stream/attack-on-titan'}
]
with patch('src.server.services.setup_service.get_series_app', return_value=mock_series_app):
result = await SetupService._resolve_key_via_search("Attack on Titan")
assert result == 'attack-on-titan'
Mocking aiohttp Sessions
When testing code that uses aiohttp.ClientSession:
from unittest.mock import AsyncMock, MagicMock, patch
from aiohttp import ClientSession
# Mock aiohttp session for testing
class MockAiohttpSession:
def __init__(self):
self.closed = False
async def close(self):
self.closed = True
def get(self, url, **kwargs):
mock_response = AsyncMock()
mock_response.status = 200
mock_response.json = AsyncMock(return_value={"data": "test"})
mock_response.__aenter__ = AsyncMock(return_value=mock_response)
mock_response.__aexit__ = AsyncMock(return_value=None)
return mock_response
# Use in fixture
@pytest.fixture
async def mock_tmdb_session():
session = MockAiohttpSession()
yield session
# Cleanup verification
assert session.closed, "Session was not closed"
Key points:
- Always verify
session.closedisTrueafter context manager exits - Mock
__aenter__and__aexit__for response context managers - Set
closed = Falseon mock session for unclosed warning tests
- Coverage Requirements
- CI/CD Integration
- Writing Good Tests
- Arrange-Act-Assert pattern
- Test isolation
- Edge cases
- Common Pitfalls to Avoid