Initial commit
This commit is contained in:
346
skills/cli-testing-patterns/templates/pytest-fixtures.py
Normal file
346
skills/cli-testing-patterns/templates/pytest-fixtures.py
Normal file
@@ -0,0 +1,346 @@
|
||||
"""
|
||||
Pytest Fixtures Template
|
||||
|
||||
Reusable pytest fixtures for CLI testing with Click.testing.CliRunner
|
||||
Provides common setup, teardown, and test utilities
|
||||
"""
|
||||
|
||||
import pytest
|
||||
import os
|
||||
import tempfile
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
from click.testing import CliRunner
|
||||
from mycli.cli import cli
|
||||
|
||||
|
||||
# Basic Fixtures
|
||||
|
||||
@pytest.fixture
|
||||
def runner():
|
||||
"""Create a CliRunner instance for testing"""
|
||||
return CliRunner()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def isolated_runner():
|
||||
"""Create a CliRunner with isolated filesystem"""
|
||||
runner = CliRunner()
|
||||
with runner.isolated_filesystem():
|
||||
yield runner
|
||||
|
||||
|
||||
# Configuration Fixtures
|
||||
|
||||
@pytest.fixture
|
||||
def temp_config_dir(tmp_path):
|
||||
"""Create a temporary configuration directory"""
|
||||
config_dir = tmp_path / '.mycli'
|
||||
config_dir.mkdir()
|
||||
return config_dir
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def config_file(temp_config_dir):
|
||||
"""Create a temporary configuration file"""
|
||||
config_path = temp_config_dir / 'config.yaml'
|
||||
config_content = """
|
||||
api_key: your_test_key_here
|
||||
environment: development
|
||||
verbose: false
|
||||
timeout: 30
|
||||
"""
|
||||
config_path.write_text(config_content)
|
||||
return config_path
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def env_with_config(temp_config_dir, monkeypatch):
|
||||
"""Set up environment with config directory"""
|
||||
monkeypatch.setenv('MYCLI_CONFIG_DIR', str(temp_config_dir))
|
||||
return temp_config_dir
|
||||
|
||||
|
||||
# File System Fixtures
|
||||
|
||||
@pytest.fixture
|
||||
def temp_workspace(tmp_path):
|
||||
"""Create a temporary workspace directory"""
|
||||
workspace = tmp_path / 'workspace'
|
||||
workspace.mkdir()
|
||||
return workspace
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def sample_project(temp_workspace):
|
||||
"""Create a sample project structure"""
|
||||
project = temp_workspace / 'sample-project'
|
||||
project.mkdir()
|
||||
|
||||
# Create sample files
|
||||
(project / 'package.json').write_text('{"name": "sample", "version": "1.0.0"}')
|
||||
(project / 'README.md').write_text('# Sample Project')
|
||||
|
||||
src_dir = project / 'src'
|
||||
src_dir.mkdir()
|
||||
(src_dir / 'index.js').write_text('console.log("Hello, World!");')
|
||||
|
||||
return project
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def sample_files(temp_workspace):
|
||||
"""Create sample files for testing"""
|
||||
files = {
|
||||
'input.txt': 'test input data\n',
|
||||
'config.yaml': 'key: value\n',
|
||||
'data.json': '{"id": 1, "name": "test"}\n'
|
||||
}
|
||||
|
||||
created_files = {}
|
||||
for filename, content in files.items():
|
||||
file_path = temp_workspace / filename
|
||||
file_path.write_text(content)
|
||||
created_files[filename] = file_path
|
||||
|
||||
return created_files
|
||||
|
||||
|
||||
# Mock Fixtures
|
||||
|
||||
@pytest.fixture
|
||||
def mock_api_key(monkeypatch):
|
||||
"""Mock API key environment variable"""
|
||||
monkeypatch.setenv('MYCLI_API_KEY', 'test_api_key_123')
|
||||
return 'test_api_key_123'
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_home_dir(tmp_path, monkeypatch):
|
||||
"""Mock home directory"""
|
||||
home = tmp_path / 'home'
|
||||
home.mkdir()
|
||||
monkeypatch.setenv('HOME', str(home))
|
||||
return home
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_no_config(monkeypatch):
|
||||
"""Remove all configuration environment variables"""
|
||||
vars_to_remove = [
|
||||
'MYCLI_CONFIG_DIR',
|
||||
'MYCLI_API_KEY',
|
||||
'MYCLI_ENVIRONMENT',
|
||||
]
|
||||
for var in vars_to_remove:
|
||||
monkeypatch.delenv(var, raising=False)
|
||||
|
||||
|
||||
# State Management Fixtures
|
||||
|
||||
@pytest.fixture
|
||||
def cli_state(temp_workspace):
|
||||
"""Create a CLI state file"""
|
||||
state_file = temp_workspace / '.mycli-state'
|
||||
state = {
|
||||
'initialized': True,
|
||||
'last_command': None,
|
||||
'history': []
|
||||
}
|
||||
import json
|
||||
state_file.write_text(json.dumps(state, indent=2))
|
||||
return state_file
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def clean_state(temp_workspace):
|
||||
"""Ensure no state file exists"""
|
||||
state_file = temp_workspace / '.mycli-state'
|
||||
if state_file.exists():
|
||||
state_file.unlink()
|
||||
return temp_workspace
|
||||
|
||||
|
||||
# Helper Function Fixtures
|
||||
|
||||
@pytest.fixture
|
||||
def run_cli_command(runner):
|
||||
"""Helper function to run CLI commands and return parsed results"""
|
||||
def _run(args, input_data=None, env=None):
|
||||
"""
|
||||
Run a CLI command and return structured results
|
||||
|
||||
Args:
|
||||
args: List of command arguments
|
||||
input_data: Optional input for interactive prompts
|
||||
env: Optional environment variables dict
|
||||
|
||||
Returns:
|
||||
dict with keys: exit_code, output, lines, success
|
||||
"""
|
||||
result = runner.invoke(cli, args, input=input_data, env=env)
|
||||
return {
|
||||
'exit_code': result.exit_code,
|
||||
'output': result.output,
|
||||
'lines': result.output.splitlines(),
|
||||
'success': result.exit_code == 0
|
||||
}
|
||||
return _run
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def assert_cli_success(runner):
|
||||
"""Helper to assert successful CLI execution"""
|
||||
def _assert(args, expected_in_output=None):
|
||||
"""
|
||||
Run CLI command and assert success
|
||||
|
||||
Args:
|
||||
args: List of command arguments
|
||||
expected_in_output: Optional string expected in output
|
||||
"""
|
||||
result = runner.invoke(cli, args)
|
||||
assert result.exit_code == 0, f"Command failed: {result.output}"
|
||||
if expected_in_output:
|
||||
assert expected_in_output in result.output
|
||||
return result
|
||||
return _assert
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def assert_cli_failure(runner):
|
||||
"""Helper to assert CLI command failure"""
|
||||
def _assert(args, expected_in_output=None):
|
||||
"""
|
||||
Run CLI command and assert failure
|
||||
|
||||
Args:
|
||||
args: List of command arguments
|
||||
expected_in_output: Optional string expected in output
|
||||
"""
|
||||
result = runner.invoke(cli, args)
|
||||
assert result.exit_code != 0, f"Command should have failed: {result.output}"
|
||||
if expected_in_output:
|
||||
assert expected_in_output in result.output
|
||||
return result
|
||||
return _assert
|
||||
|
||||
|
||||
# Cleanup Fixtures
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def cleanup_temp_files(request):
|
||||
"""Automatically clean up temporary files after tests"""
|
||||
temp_files = []
|
||||
|
||||
def _register(filepath):
|
||||
temp_files.append(filepath)
|
||||
|
||||
request.addfinalizer(lambda: [
|
||||
os.remove(f) for f in temp_files if os.path.exists(f)
|
||||
])
|
||||
|
||||
return _register
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def test_data_dir():
|
||||
"""Provide path to test data directory"""
|
||||
return Path(__file__).parent / 'test_data'
|
||||
|
||||
|
||||
# Parametrized Fixtures
|
||||
|
||||
@pytest.fixture(params=['json', 'yaml', 'table'])
|
||||
def output_format(request):
|
||||
"""Parametrize tests across different output formats"""
|
||||
return request.param
|
||||
|
||||
|
||||
@pytest.fixture(params=[True, False])
|
||||
def verbose_mode(request):
|
||||
"""Parametrize tests with and without verbose mode"""
|
||||
return request.param
|
||||
|
||||
|
||||
@pytest.fixture(params=['development', 'staging', 'production'])
|
||||
def environment(request):
|
||||
"""Parametrize tests across different environments"""
|
||||
return request.param
|
||||
|
||||
|
||||
# Integration Test Fixtures
|
||||
|
||||
@pytest.fixture
|
||||
def integration_workspace(tmp_path):
|
||||
"""
|
||||
Create a complete integration test workspace with all necessary files
|
||||
"""
|
||||
workspace = tmp_path / 'integration'
|
||||
workspace.mkdir()
|
||||
|
||||
# Create directory structure
|
||||
(workspace / 'src').mkdir()
|
||||
(workspace / 'tests').mkdir()
|
||||
(workspace / 'config').mkdir()
|
||||
(workspace / 'data').mkdir()
|
||||
|
||||
# Create config files
|
||||
(workspace / 'config' / 'dev.yaml').write_text('env: development\n')
|
||||
(workspace / 'config' / 'prod.yaml').write_text('env: production\n')
|
||||
|
||||
# Initialize CLI
|
||||
runner = CliRunner()
|
||||
with runner.isolated_filesystem(temp_dir=workspace):
|
||||
runner.invoke(cli, ['init'])
|
||||
|
||||
return workspace
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_external_service(monkeypatch):
|
||||
"""Mock external service API calls"""
|
||||
class MockService:
|
||||
def __init__(self):
|
||||
self.calls = []
|
||||
|
||||
def call_api(self, endpoint, method='GET', data=None):
|
||||
self.calls.append({
|
||||
'endpoint': endpoint,
|
||||
'method': method,
|
||||
'data': data
|
||||
})
|
||||
return {'status': 'success', 'data': 'mock response'}
|
||||
|
||||
mock = MockService()
|
||||
# Replace actual service with mock
|
||||
monkeypatch.setattr('mycli.services.api', mock)
|
||||
return mock
|
||||
|
||||
|
||||
# Snapshot Testing Fixtures
|
||||
|
||||
@pytest.fixture
|
||||
def snapshot_dir(tmp_path):
|
||||
"""Create directory for snapshot testing"""
|
||||
snapshot = tmp_path / 'snapshots'
|
||||
snapshot.mkdir()
|
||||
return snapshot
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def compare_output(snapshot_dir):
|
||||
"""Compare CLI output with saved snapshot"""
|
||||
def _compare(output, snapshot_name):
|
||||
snapshot_file = snapshot_dir / f'{snapshot_name}.txt'
|
||||
|
||||
if not snapshot_file.exists():
|
||||
# Create snapshot
|
||||
snapshot_file.write_text(output)
|
||||
return True
|
||||
|
||||
# Compare with existing snapshot
|
||||
expected = snapshot_file.read_text()
|
||||
return output == expected
|
||||
|
||||
return _compare
|
||||
Reference in New Issue
Block a user