fixing DQ issues
This commit is contained in:
352
tests_restructure.md
Normal file
352
tests_restructure.md
Normal file
@@ -0,0 +1,352 @@
|
||||
# Tests Folder Restructure Plan
|
||||
|
||||
## Current vs Recommended Structure
|
||||
|
||||
### Before (Single Folder)
|
||||
```
|
||||
tests/
|
||||
├── test_auth.py
|
||||
├── test_products.py
|
||||
├── test_stock.py
|
||||
├── test_shops.py
|
||||
├── test_marketplace.py
|
||||
├── test_admin.py
|
||||
├── test_stats.py
|
||||
├── test_database.py
|
||||
├── test_utils.py
|
||||
├── conftest.py
|
||||
└── ...more files
|
||||
```
|
||||
|
||||
### After (Organized Structure)
|
||||
```
|
||||
tests/
|
||||
├── conftest.py # Global test configuration and fixtures
|
||||
├── pytest.ini # Pytest configuration
|
||||
├── __init__.py
|
||||
├── fixtures/ # Shared test fixtures
|
||||
│ ├── __init__.py
|
||||
│ ├── auth_fixtures.py # Auth-related fixtures
|
||||
│ ├── product_fixtures.py # Product fixtures
|
||||
│ ├── shop_fixtures.py # Shop fixtures
|
||||
│ └── database_fixtures.py # Database setup fixtures
|
||||
├── unit/ # Unit tests (isolated, fast)
|
||||
│ ├── __init__.py
|
||||
│ ├── conftest.py # Unit test specific fixtures
|
||||
│ ├── models/ # Test database and API models
|
||||
│ │ ├── __init__.py
|
||||
│ │ ├── test_user_model.py
|
||||
│ │ ├── test_product_model.py
|
||||
│ │ ├── test_shop_model.py
|
||||
│ │ └── test_stock_model.py
|
||||
│ ├── utils/ # Test utility functions
|
||||
│ │ ├── __init__.py
|
||||
│ │ ├── test_data_processing.py
|
||||
│ │ ├── test_csv_processor.py
|
||||
│ │ └── test_database_utils.py
|
||||
│ ├── services/ # Test business logic
|
||||
│ │ ├── __init__.py
|
||||
│ │ ├── test_auth_service.py
|
||||
│ │ ├── test_product_service.py
|
||||
│ │ └── test_marketplace_service.py
|
||||
│ └── middleware/ # Test middleware components
|
||||
│ ├── __init__.py
|
||||
│ ├── test_auth_middleware.py
|
||||
│ ├── test_rate_limiter.py
|
||||
│ └── test_error_handler.py
|
||||
├── integration/ # Integration tests (multiple components)
|
||||
│ ├── __init__.py
|
||||
│ ├── conftest.py # Integration test fixtures
|
||||
│ ├── api/ # API endpoint tests
|
||||
│ │ ├── __init__.py
|
||||
│ │ ├── conftest.py # API test fixtures (test client, etc.)
|
||||
│ │ ├── v1/
|
||||
│ │ │ ├── __init__.py
|
||||
│ │ │ ├── test_auth_endpoints.py
|
||||
│ │ │ ├── test_product_endpoints.py
|
||||
│ │ │ ├── test_shop_endpoints.py
|
||||
│ │ │ ├── test_stock_endpoints.py
|
||||
│ │ │ ├── test_marketplace_endpoints.py
|
||||
│ │ │ ├── test_admin_endpoints.py
|
||||
│ │ │ └── test_stats_endpoints.py
|
||||
│ │ └── test_api_main.py # Test API router setup
|
||||
│ ├── database/ # Database integration tests
|
||||
│ │ ├── __init__.py
|
||||
│ │ ├── test_crud_operations.py
|
||||
│ │ ├── test_relationships.py
|
||||
│ │ └── test_migrations.py
|
||||
│ └── workflows/ # End-to-end workflow tests
|
||||
│ ├── __init__.py
|
||||
│ ├── test_product_import_workflow.py
|
||||
│ ├── test_shop_setup_workflow.py
|
||||
│ └── test_stock_management_workflow.py
|
||||
├── e2e/ # End-to-end tests (full application)
|
||||
│ ├── __init__.py
|
||||
│ ├── conftest.py
|
||||
│ ├── test_user_registration_flow.py
|
||||
│ ├── test_marketplace_import_flow.py
|
||||
│ └── test_complete_shop_setup.py
|
||||
├── performance/ # Performance and load tests
|
||||
│ ├── __init__.py
|
||||
│ ├── test_api_performance.py
|
||||
│ ├── test_database_performance.py
|
||||
│ └── test_import_performance.py
|
||||
└── test_data/ # Test data files
|
||||
├── csv/
|
||||
│ ├── valid_products.csv
|
||||
│ ├── invalid_products.csv
|
||||
│ └── large_dataset.csv
|
||||
├── json/
|
||||
│ ├── sample_product.json
|
||||
│ └── marketplace_response.json
|
||||
└── fixtures/
|
||||
├── test_users.json
|
||||
└── test_products.json
|
||||
```
|
||||
|
||||
## File Organization Principles
|
||||
|
||||
### 1. Test Types Separation
|
||||
- **Unit Tests**: Fast, isolated tests for individual functions/classes
|
||||
- **Integration Tests**: Tests that involve multiple components working together
|
||||
- **E2E Tests**: Full application flow tests
|
||||
- **Performance Tests**: Load and performance testing
|
||||
|
||||
### 2. Fixture Organization
|
||||
```python
|
||||
# tests/fixtures/auth_fixtures.py
|
||||
import pytest
|
||||
from models.database import User
|
||||
from utils.auth import create_access_token
|
||||
|
||||
@pytest.fixture
|
||||
def test_user_data():
|
||||
return {
|
||||
"email": "test@example.com",
|
||||
"username": "testuser",
|
||||
"password": "testpass123"
|
||||
}
|
||||
|
||||
@pytest.fixture
|
||||
def authenticated_user(db_session, test_user_data):
|
||||
user = User(**test_user_data)
|
||||
db_session.add(user)
|
||||
db_session.commit()
|
||||
return user
|
||||
|
||||
@pytest.fixture
|
||||
def auth_headers(authenticated_user):
|
||||
token = create_access_token(data={"sub": authenticated_user.username})
|
||||
return {"Authorization": f"Bearer {token}"}
|
||||
```
|
||||
|
||||
### 3. Conftest.py Structure
|
||||
```python
|
||||
# tests/conftest.py (Global fixtures)
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
|
||||
from main import app
|
||||
from app.core.database import get_db, Base
|
||||
|
||||
# Database fixtures
|
||||
@pytest.fixture(scope="session")
|
||||
def test_engine():
|
||||
engine = create_engine("sqlite:///test.db")
|
||||
Base.metadata.create_all(bind=engine)
|
||||
yield engine
|
||||
Base.metadata.drop_all(bind=engine)
|
||||
|
||||
@pytest.fixture
|
||||
def db_session(test_engine):
|
||||
TestingSessionLocal = sessionmaker(bind=test_engine)
|
||||
session = TestingSessionLocal()
|
||||
yield session
|
||||
session.close()
|
||||
|
||||
@pytest.fixture
|
||||
def client(db_session):
|
||||
def override_get_db():
|
||||
yield db_session
|
||||
|
||||
app.dependency_overrides[get_db] = override_get_db
|
||||
yield TestClient(app)
|
||||
app.dependency_overrides.clear()
|
||||
```
|
||||
|
||||
```python
|
||||
# tests/integration/api/conftest.py (API-specific fixtures)
|
||||
import pytest
|
||||
|
||||
@pytest.fixture
|
||||
def api_client(client):
|
||||
"""Pre-configured API client for integration tests"""
|
||||
return client
|
||||
|
||||
@pytest.fixture
|
||||
def admin_client(client, admin_auth_headers):
|
||||
"""API client with admin authentication"""
|
||||
client.headers.update(admin_auth_headers)
|
||||
return client
|
||||
```
|
||||
|
||||
## Test Naming Conventions
|
||||
|
||||
### File Naming
|
||||
- `test_*.py` for all test files
|
||||
- Mirror your app structure: `test_product_endpoints.py` for `api/v1/products.py`
|
||||
- Use descriptive names: `test_marketplace_import_workflow.py`
|
||||
|
||||
### Test Function Naming
|
||||
```python
|
||||
# Good test naming patterns
|
||||
def test_create_product_with_valid_data_returns_201():
|
||||
pass
|
||||
|
||||
def test_create_product_without_title_returns_422():
|
||||
pass
|
||||
|
||||
def test_get_product_by_id_returns_product_data():
|
||||
pass
|
||||
|
||||
def test_get_nonexistent_product_returns_404():
|
||||
pass
|
||||
|
||||
def test_update_product_stock_updates_quantity():
|
||||
pass
|
||||
|
||||
def test_marketplace_import_with_invalid_csv_fails_gracefully():
|
||||
pass
|
||||
```
|
||||
|
||||
## Running Tests by Category
|
||||
|
||||
### Pytest Configuration (pytest.ini)
|
||||
```ini
|
||||
[tool:pytest]
|
||||
testpaths = tests
|
||||
python_files = test_*.py
|
||||
python_classes = Test*
|
||||
python_functions = test_*
|
||||
addopts =
|
||||
-v
|
||||
--tb=short
|
||||
--strict-markers
|
||||
markers =
|
||||
unit: Unit tests
|
||||
integration: Integration tests
|
||||
e2e: End-to-end tests
|
||||
performance: Performance tests
|
||||
slow: Slow running tests
|
||||
database: Tests that require database
|
||||
external: Tests that require external services
|
||||
```
|
||||
|
||||
### Running Specific Test Categories
|
||||
```bash
|
||||
# Run only unit tests (fast)
|
||||
pytest tests/unit -m unit
|
||||
|
||||
# Run only integration tests
|
||||
pytest tests/integration -m integration
|
||||
|
||||
# Run all tests except slow ones
|
||||
pytest -m "not slow"
|
||||
|
||||
# Run only database tests
|
||||
pytest -m database
|
||||
|
||||
# Run tests for specific domain
|
||||
pytest tests/unit/models/ tests/integration/api/v1/test_product_endpoints.py
|
||||
|
||||
# Run with coverage
|
||||
pytest --cov=app --cov-report=html
|
||||
```
|
||||
|
||||
## Benefits of This Structure
|
||||
|
||||
1. **Faster Development**: Developers can run relevant test subsets
|
||||
2. **Clear Separation**: Easy to understand what each test covers
|
||||
3. **Parallel Execution**: Can run different test types in parallel
|
||||
4. **Maintenance**: Easier to maintain and update tests
|
||||
5. **CI/CD Pipeline**: Can have different pipeline stages for different test types
|
||||
|
||||
## Migration Strategy
|
||||
|
||||
### Phase 1: Create Structure
|
||||
1. Create new directory structure
|
||||
2. Move global fixtures to `fixtures/` directory
|
||||
3. Update `conftest.py` files
|
||||
|
||||
### Phase 2: Move Unit Tests
|
||||
1. Start with utility and model tests
|
||||
2. Move to `tests/unit/` with appropriate subdirectories
|
||||
3. Update imports and fixtures
|
||||
|
||||
### Phase 3: Move Integration Tests
|
||||
1. Move API endpoint tests to `tests/integration/api/`
|
||||
2. Create database integration tests
|
||||
3. Add workflow tests
|
||||
|
||||
### Phase 4: Add Missing Coverage
|
||||
1. Add performance tests if needed
|
||||
2. Create E2E tests for critical flows
|
||||
3. Add proper test markers
|
||||
|
||||
## Example Test File Structure
|
||||
|
||||
### Unit Test Example
|
||||
```python
|
||||
# tests/unit/models/test_product_model.py
|
||||
import pytest
|
||||
from models.database import Product
|
||||
|
||||
class TestProductModel:
|
||||
def test_product_creation_with_valid_data(self, db_session):
|
||||
product = Product(
|
||||
product_id="TEST123",
|
||||
title="Test Product",
|
||||
price="99.99"
|
||||
)
|
||||
db_session.add(product)
|
||||
db_session.commit()
|
||||
|
||||
assert product.id is not None
|
||||
assert product.product_id == "TEST123"
|
||||
assert product.title == "Test Product"
|
||||
|
||||
def test_product_gtin_relationship_with_stock(self, db_session, test_product, test_stock):
|
||||
# Test the GTIN-based relationship
|
||||
assert test_product.gtin in [stock.gtin for stock in test_product.stock_entries]
|
||||
```
|
||||
|
||||
### Integration Test Example
|
||||
```python
|
||||
# tests/integration/api/v1/test_product_endpoints.py
|
||||
import pytest
|
||||
|
||||
class TestProductEndpoints:
|
||||
def test_create_product_endpoint(self, client, auth_headers, valid_product_data):
|
||||
response = client.post(
|
||||
"/api/v1/products/",
|
||||
json=valid_product_data,
|
||||
headers=auth_headers
|
||||
)
|
||||
assert response.status_code == 201
|
||||
assert response.json()["product_id"] == valid_product_data["product_id"]
|
||||
|
||||
def test_get_products_with_pagination(self, client, auth_headers, multiple_products):
|
||||
response = client.get(
|
||||
"/api/v1/products/?skip=0&limit=10",
|
||||
headers=auth_headers
|
||||
)
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert "products" in data
|
||||
assert "total" in data
|
||||
assert len(data["products"]) <= 10
|
||||
```
|
||||
|
||||
This structure will scale beautifully as your application grows and makes it much easier for your team to maintain and extend the test suite!
|
||||
Reference in New Issue
Block a user