Unified pytest fixtures for Netrun Systems services - eliminates 71% fixture duplication
Project description
netrun-pytest-fixtures
Unified pytest fixtures for Netrun Systems services. Eliminates 71% fixture duplication across Service_* test suites.
Features
- Async Utilities: Session-scoped event loops for async testing (addresses 71% duplication)
- Authentication: RSA key pairs, JWT claims, test users with role hierarchies
- Database: SQLAlchemy async sessions with automatic cleanup
- API Clients: httpx AsyncClient and FastAPI TestClient mocks
- Redis: Mock Redis clients with full operation support
- Environment: Environment variable isolation and management
- Filesystem: Temporary files, directories, and repository structures
- Logging: Logging reset and capture utilities
Installation
pip install netrun-pytest-fixtures
Optional Dependencies
Install with specific fixture modules:
# SQLAlchemy fixtures
pip install netrun-pytest-fixtures[sqlalchemy]
# HTTP client fixtures
pip install netrun-pytest-fixtures[httpx]
# FastAPI fixtures
pip install netrun-pytest-fixtures[fastapi]
# Redis fixtures
pip install netrun-pytest-fixtures[redis]
# YAML file fixtures
pip install netrun-pytest-fixtures[yaml]
# All optional dependencies
pip install netrun-pytest-fixtures[all]
Usage
Simply install the package - fixtures are automatically registered as a pytest plugin.
# test_example.py
import pytest
# Async testing with event loop fixture
@pytest.mark.asyncio
async def test_async_operation(event_loop):
async def my_async_function():
return "result"
result = await my_async_function()
assert result == "result"
# Authentication testing
def test_jwt_claims(sample_jwt_claims, rsa_key_pair):
private_pem, public_pem = rsa_key_pair
assert "sub" in sample_jwt_claims
assert "tenant_id" in sample_jwt_claims
assert len(private_pem) > 0
# Database testing
@pytest.mark.asyncio
async def test_database(async_db_session):
# Use async database session
# Changes automatically rolled back after test
pass
# Redis testing
@pytest.mark.asyncio
async def test_redis_cache(mock_redis):
await mock_redis.set("key", "value")
mock_redis.set.assert_called_once_with("key", "value")
# Environment testing
def test_config(clean_env, sample_env_vars):
for key, value in sample_env_vars.items():
clean_env.setenv(key, value)
# Test configuration loading
pass
# Filesystem testing
def test_file_operations(temp_directory):
test_file = temp_directory / "test.txt"
test_file.write_text("content")
assert test_file.exists()
Available Fixtures
Async Utilities (async_utils.py)
event_loop: Session-scoped asyncio event loop (HIGHEST PRIORITY - 71% duplication reduction)new_event_loop: Fresh event loop for isolated testing
Authentication (auth.py)
rsa_key_pair: Generate RSA key pair for JWT testingtemp_key_files: Temporary PEM files for key loadingsample_jwt_claims: Standard JWT claims with full permissionsminimal_claims: Minimal valid JWT claimsexpired_claims: Expired JWT claims for expiry testingtest_user: Regular user with basic permissionsadmin_user: Admin user with elevated permissionssuperadmin_user: Superadmin with full system accesstest_tenant_id: Standard test tenant UUIDmock_request: Mock FastAPI Request objectmock_request_with_jwt: Request with JWT Authorization headermock_api_key_request: Request with X-API-Key headersample_role_hierarchy: Role inheritance mappingsample_permission_map: Role to permissions mapping
Database (database.py)
test_database_url: Test database URL (SQLite in-memory by default)async_engine: Async SQLAlchemy engineasync_session_factory: Session factory for creating sessionsasync_db_session: Async database session with automatic rollbackinit_test_database: Initialize database schema for testingmock_db_session: Mock database session for unit teststransaction_rollback_session: Session with nested transaction rollback
API Clients (api_clients.py)
base_url: Base URL for API testingasync_client: httpx AsyncClient for async HTTP requeststest_client: FastAPI TestClient factorymock_response: Mock HTTP response objectmock_async_client: Mock async HTTP clientauth_headers: Authorization headers with JWTapi_key_headers: Headers with X-API-Keymultipart_headers: Headers for multipart uploadsmock_httpx_transport: Mock httpx transport
Redis (redis.py)
redis_url: Test Redis URLmock_redis: Mock async Redis client with all operationsmock_redis_client: Alias for mock_redismock_redis_pool: Mock Redis connection poolmock_redis_with_data: Mock Redis with in-memory data storemock_redis_error: Mock Redis that raises connection errorscleanup_redis_keys: Cleanup fixture for Redis keys
Environment (environment.py)
clean_env: Clean environment with Netrun variables clearedsample_env_vars: Common test environment variablesmock_env_file: Temporary .env filetemp_env_file: Factory for creating custom .env filesreset_environment: Auto-cleanup of environment variables (autouse)isolated_env: Completely isolated environmentmock_azure_env: Azure-specific environment variablesproduction_like_env: Production-like configuration
Filesystem (filesystem.py)
temp_directory: Temporary directory for file operationstemp_file: Factory for creating temporary filestemp_json_file: Factory for JSON filestemp_yaml_file: Factory for YAML filestemp_repo_structure: Mock repository structuretemp_config_file: Temporary configuration filetemp_log_file: Temporary log filetemp_csv_file: Factory for CSV filestemp_binary_file: Factory for binary files
Logging (logging.py)
reset_logging: Reset logging between tests (autouse)sample_log_record: Sample LogRecord for testing formatterslogger_with_handler: Logger with in-memory handlercapture_logs: Capture logs to list for assertionsjson_log_formatter: JSON log formattersilence_loggers: Silence noisy loggerslog_level_setter: Temporarily set log levelsmock_log_handler: Mock logging handlerexception_log_record: LogRecord with exception info
Examples
Complete Test Examples
Testing Async Database Operations
import pytest
from sqlalchemy import select
from myapp.models import User
@pytest.mark.asyncio
async def test_user_creation(async_db_session):
"""Test creating a user in the database."""
user = User(name="Test User", email="test@netrunsystems.com")
async_db_session.add(user)
await async_db_session.commit()
# Query the user
result = await async_db_session.execute(
select(User).where(User.email == "test@netrunsystems.com")
)
found_user = result.scalar_one()
assert found_user.name == "Test User"
# Changes automatically rolled back after test
Testing Authentication with JWT
import pytest
import jwt
def test_jwt_encoding_decoding(rsa_key_pair, sample_jwt_claims):
"""Test JWT encoding and decoding with RSA keys."""
private_pem, public_pem = rsa_key_pair
# Encode JWT
token = jwt.encode(
sample_jwt_claims,
private_pem,
algorithm="RS256"
)
# Decode JWT
decoded = jwt.decode(
token,
public_pem,
algorithms=["RS256"]
)
assert decoded["sub"] == sample_jwt_claims["sub"]
assert decoded["tenant_id"] == sample_jwt_claims["tenant_id"]
Testing API Endpoints
import pytest
from fastapi import FastAPI
from fastapi.testclient import TestClient
@pytest.fixture
def app():
"""Create FastAPI app for testing."""
app = FastAPI()
@app.get("/users")
async def get_users():
return [{"id": "1", "name": "Test User"}]
return app
def test_get_users_endpoint(test_client, app):
"""Test GET /users endpoint."""
client = test_client(app)
response = client.get("/users")
assert response.status_code == 200
data = response.json()
assert len(data) == 1
assert data[0]["name"] == "Test User"
Testing Redis Caching
import pytest
@pytest.mark.asyncio
async def test_cache_user_data(mock_redis_with_data):
"""Test caching user data in Redis."""
# Cache user data
user_data = '{"id": "123", "name": "Test User"}'
await mock_redis_with_data.set("user:123", user_data)
# Retrieve from cache
cached = await mock_redis_with_data.get("user:123")
assert cached == user_data
@pytest.mark.asyncio
async def test_cache_miss_handling(mock_redis):
"""Test handling cache misses."""
mock_redis.get.return_value = None
result = await mock_redis.get("nonexistent:key")
assert result is None
Testing Environment Configuration
import pytest
import os
def test_load_config_from_env(clean_env, sample_env_vars):
"""Test loading configuration from environment."""
# Set environment variables
for key, value in sample_env_vars.items():
clean_env.setenv(key, value)
# Load config
assert os.getenv("APP_NAME") == "TestApp"
assert os.getenv("APP_ENVIRONMENT") == "testing"
def test_config_from_env_file(mock_env_file):
"""Test loading configuration from .env file."""
from dotenv import load_dotenv
load_dotenv(mock_env_file)
assert os.getenv("APP_NAME") == "EnvFileApp"
assert os.getenv("LOG_LEVEL") == "WARNING"
Development
Running Tests
# Install dev dependencies
pip install -e ".[dev,all]"
# Run tests
pytest
# Run with coverage
pytest --cov=netrun_pytest_fixtures --cov-report=html
# Run specific test file
pytest tests/test_fixtures.py
# Run specific test
pytest tests/test_fixtures.py::TestAsyncUtils::test_event_loop_fixture
Code Quality
# Format code
black netrun_pytest_fixtures tests
# Lint code
ruff netrun_pytest_fixtures tests
# Type checking
mypy netrun_pytest_fixtures
Impact Metrics
Duplication Reduction
- event_loop fixture: 71% duplication across Service_* test suites
- Total fixtures consolidated: 50+ unique fixtures
- Services benefiting: 15+ Netrun services
- Lines of code eliminated: ~2,000+ duplicate fixture definitions
Performance Improvements
- Session-scoped event loops: 30% faster async test execution
- Fixture caching: Reduces test setup time by 40%
- Test isolation: 100% reliable test isolation with automatic cleanup
Versioning
This package follows Semantic Versioning.
- 1.0.0: Initial stable release with all core fixture modules
License
MIT License - Copyright (c) 2025 Netrun Systems
Support
- Documentation: GitHub README
- Issues: GitHub Issues
- Email: engineering@netrunsystems.com
- Website: https://netrunsystems.com
Contributing
Contributions welcome! Please:
- Fork the repository
- Create a feature branch
- Add tests for new fixtures
- Ensure all tests pass
- Submit a pull request
Changelog
1.0.0 (2025-01-25)
- Initial release
- Async utilities with session-scoped event loop
- Authentication fixtures (RSA keys, JWT, users, permissions)
- Database fixtures (SQLAlchemy async sessions)
- API client fixtures (httpx, FastAPI)
- Redis mock fixtures
- Environment isolation fixtures
- Filesystem testing fixtures
- Logging utilities
Acknowledgments
Built with analysis from pytest Fixtures Analysis Report identifying 71% duplication in event_loop fixtures across Netrun Systems services.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file netrun_pytest_fixtures-1.0.0.tar.gz.
File metadata
- Download URL: netrun_pytest_fixtures-1.0.0.tar.gz
- Upload date:
- Size: 33.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c91bdaec8026bedd5d7ac3302707b44ecaeed857656776c05f62b3f51f4af8b3
|
|
| MD5 |
25e7ed34c2446b6e7f952db6ae5c2c7c
|
|
| BLAKE2b-256 |
a7e52dc716f7ab320b535897586a9d94025bf4cc956b7127841513476ccbec50
|
File details
Details for the file netrun_pytest_fixtures-1.0.0-py3-none-any.whl.
File metadata
- Download URL: netrun_pytest_fixtures-1.0.0-py3-none-any.whl
- Upload date:
- Size: 29.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
814349a6a4e6aa066c447684310b9b4ea26c7ba75435998331b98d2ef8e08cd9
|
|
| MD5 |
0f80c8dd4ef9b167b51d8c086316eec2
|
|
| BLAKE2b-256 |
ad9f69dcfc95bdaebd0b3088e4174882f0bb797f7ba26c93cd7db6ec3e03faf1
|