347 lines
8.6 KiB
Python
347 lines
8.6 KiB
Python
"""
|
|
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
|