""" 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