Skip to main content

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 testing
  • temp_key_files: Temporary PEM files for key loading
  • sample_jwt_claims: Standard JWT claims with full permissions
  • minimal_claims: Minimal valid JWT claims
  • expired_claims: Expired JWT claims for expiry testing
  • test_user: Regular user with basic permissions
  • admin_user: Admin user with elevated permissions
  • superadmin_user: Superadmin with full system access
  • test_tenant_id: Standard test tenant UUID
  • mock_request: Mock FastAPI Request object
  • mock_request_with_jwt: Request with JWT Authorization header
  • mock_api_key_request: Request with X-API-Key header
  • sample_role_hierarchy: Role inheritance mapping
  • sample_permission_map: Role to permissions mapping

Database (database.py)

  • test_database_url: Test database URL (SQLite in-memory by default)
  • async_engine: Async SQLAlchemy engine
  • async_session_factory: Session factory for creating sessions
  • async_db_session: Async database session with automatic rollback
  • init_test_database: Initialize database schema for testing
  • mock_db_session: Mock database session for unit tests
  • transaction_rollback_session: Session with nested transaction rollback

API Clients (api_clients.py)

  • base_url: Base URL for API testing
  • async_client: httpx AsyncClient for async HTTP requests
  • test_client: FastAPI TestClient factory
  • mock_response: Mock HTTP response object
  • mock_async_client: Mock async HTTP client
  • auth_headers: Authorization headers with JWT
  • api_key_headers: Headers with X-API-Key
  • multipart_headers: Headers for multipart uploads
  • mock_httpx_transport: Mock httpx transport

Redis (redis.py)

  • redis_url: Test Redis URL
  • mock_redis: Mock async Redis client with all operations
  • mock_redis_client: Alias for mock_redis
  • mock_redis_pool: Mock Redis connection pool
  • mock_redis_with_data: Mock Redis with in-memory data store
  • mock_redis_error: Mock Redis that raises connection errors
  • cleanup_redis_keys: Cleanup fixture for Redis keys

Environment (environment.py)

  • clean_env: Clean environment with Netrun variables cleared
  • sample_env_vars: Common test environment variables
  • mock_env_file: Temporary .env file
  • temp_env_file: Factory for creating custom .env files
  • reset_environment: Auto-cleanup of environment variables (autouse)
  • isolated_env: Completely isolated environment
  • mock_azure_env: Azure-specific environment variables
  • production_like_env: Production-like configuration

Filesystem (filesystem.py)

  • temp_directory: Temporary directory for file operations
  • temp_file: Factory for creating temporary files
  • temp_json_file: Factory for JSON files
  • temp_yaml_file: Factory for YAML files
  • temp_repo_structure: Mock repository structure
  • temp_config_file: Temporary configuration file
  • temp_log_file: Temporary log file
  • temp_csv_file: Factory for CSV files
  • temp_binary_file: Factory for binary files

Logging (logging.py)

  • reset_logging: Reset logging between tests (autouse)
  • sample_log_record: Sample LogRecord for testing formatters
  • logger_with_handler: Logger with in-memory handler
  • capture_logs: Capture logs to list for assertions
  • json_log_formatter: JSON log formatter
  • silence_loggers: Silence noisy loggers
  • log_level_setter: Temporarily set log levels
  • mock_log_handler: Mock logging handler
  • exception_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

Contributing

Contributions welcome! Please:

  1. Fork the repository
  2. Create a feature branch
  3. Add tests for new fixtures
  4. Ensure all tests pass
  5. 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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

netrun_pytest_fixtures-2.1.0.tar.gz (47.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

netrun_pytest_fixtures-2.1.0-py3-none-any.whl (54.6 kB view details)

Uploaded Python 3

File details

Details for the file netrun_pytest_fixtures-2.1.0.tar.gz.

File metadata

  • Download URL: netrun_pytest_fixtures-2.1.0.tar.gz
  • Upload date:
  • Size: 47.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.12

File hashes

Hashes for netrun_pytest_fixtures-2.1.0.tar.gz
Algorithm Hash digest
SHA256 96418922450e6ec2383ba78189c5c1537a62d73f792ffe05a6e478f8d0b1d58a
MD5 4683fa889f96eb9776de293173adc8c5
BLAKE2b-256 d755e2f607650e490b70169e9c7a00df786176536c2b2c642551c00e81494e1c

See more details on using hashes here.

File details

Details for the file netrun_pytest_fixtures-2.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for netrun_pytest_fixtures-2.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 15bae7d730a35745b7d9278a7abdf9c420823dc6dff543e21bd50074d8b411ab
MD5 09d50cd260202be6a6495f0958ad96f0
BLAKE2b-256 153068fc39951444ff1d9b4e8bb58edf9fee159f0cf61f9f0e4f211717c950b8

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page