|
| 1 | +# ACCESS-MOPPeR Test Suite |
| 2 | + |
| 3 | +This directory contains the comprehensive test suite for ACCESS-MOPPeR. |
| 4 | + |
| 5 | +## Test Structure |
| 6 | + |
| 7 | +### 📁 Test Organization |
| 8 | + |
| 9 | +``` |
| 10 | +tests/ |
| 11 | +├── conftest.py # Shared fixtures and utilities |
| 12 | +├── pytest.ini # Test configuration |
| 13 | +├── test_smoke.py # Basic smoke tests |
| 14 | +├── test_mop.py # Legacy (deprecated) |
| 15 | +├── unit/ # Unit tests |
| 16 | +│ ├── test_base.py # BaseCMORiser tests |
| 17 | +│ ├── test_batch_cmoriser.py # Batch processing tests |
| 18 | +│ ├── test_templates.py # Template tests |
| 19 | +│ └── test_tracking.py # Tracking functionality tests |
| 20 | +├── integration/ # Integration tests |
| 21 | +│ ├── test_cmoriser_integration.py # CMORiser integration |
| 22 | +│ ├── test_batch_integration.py # Batch processing integration |
| 23 | +│ └── test_full_cmorisation.py # Full CMOR workflow tests |
| 24 | +├── e2e/ # End-to-end tests |
| 25 | +│ └── test_end_to_end.py # Real data processing tests |
| 26 | +├── performance/ # Performance and memory tests |
| 27 | +│ └── test_memory_usage.py # Memory usage and optimization tests |
| 28 | +├── mocks/ # Mock data and utilities |
| 29 | +│ ├── mock_data.py # Mock dataset generators |
| 30 | +│ ├── mock_files.py # Mock file utilities |
| 31 | +│ └── mock_pbs.py # Mock PBS/job scheduler |
| 32 | +├── data/ # Test data files |
| 33 | +└── scripts/ # Test utilities and scripts |
| 34 | +``` |
| 35 | + |
| 36 | +## 🏃 Running Tests |
| 37 | + |
| 38 | +### Run all tests |
| 39 | +```bash |
| 40 | +pytest tests/ |
| 41 | +``` |
| 42 | + |
| 43 | +### Run by category |
| 44 | +```bash |
| 45 | +# Unit tests (fast) |
| 46 | +pytest tests/unit/ -m unit |
| 47 | + |
| 48 | +# Integration tests (medium speed) |
| 49 | +pytest tests/integration/ -m integration |
| 50 | + |
| 51 | +# End-to-end tests (slow, requires test data) |
| 52 | +pytest tests/e2e/ -m e2e |
| 53 | + |
| 54 | +# Performance tests (very slow) |
| 55 | +pytest tests/performance/ -m performance |
| 56 | +``` |
| 57 | + |
| 58 | +### Run by speed |
| 59 | +```bash |
| 60 | +# Fast tests only (good for development) |
| 61 | +pytest tests/ -m "not slow" |
| 62 | + |
| 63 | +# Include slow tests (good for CI) |
| 64 | +pytest tests/ -m "slow or not slow" |
| 65 | +``` |
| 66 | + |
| 67 | +### Run smoke tests |
| 68 | +```bash |
| 69 | +# Quick verification that basic functionality works |
| 70 | +pytest tests/test_smoke.py |
| 71 | +``` |
| 72 | + |
| 73 | +## 🏷️ Test Markers |
| 74 | + |
| 75 | +Tests are marked with the following categories: |
| 76 | + |
| 77 | +- `unit`: Unit tests (fast, isolated) |
| 78 | +- `integration`: Integration tests (medium speed, may use mocks) |
| 79 | +- `e2e`: End-to-end tests (slow, requires real data) |
| 80 | +- `slow`: Tests that take significant time |
| 81 | +- `performance`: Performance and memory benchmarks |
| 82 | +- `memory`: Memory usage tests |
| 83 | + |
| 84 | +## 📊 Test Coverage |
| 85 | + |
| 86 | +Run tests with coverage reporting: |
| 87 | +```bash |
| 88 | +pytest tests/ --cov=access_mopper --cov-report=html --cov-report=term |
| 89 | +``` |
| 90 | + |
| 91 | +View coverage report: |
| 92 | +```bash |
| 93 | +open htmlcov/index.html |
| 94 | +``` |
| 95 | + |
| 96 | +## 🔧 Test Configuration |
| 97 | + |
| 98 | +Key configuration in `pytest.ini`: |
| 99 | +- Test discovery patterns |
| 100 | +- Marker definitions |
| 101 | +- Warning filters |
| 102 | +- Minimum pytest version requirements |
| 103 | + |
| 104 | +Shared fixtures in `conftest.py`: |
| 105 | +- `temp_dir`: Temporary directory for test outputs |
| 106 | +- `parent_experiment_config`: Standard parent experiment metadata |
| 107 | +- `mock_netcdf_dataset`: Mock xarray dataset |
| 108 | +- `mock_config`: Standard CMIP6 configuration |
| 109 | +- `batch_config`: Batch processing configuration |
| 110 | + |
| 111 | +## 📝 Test Data |
| 112 | + |
| 113 | +Test data is organized in `tests/data/`: |
| 114 | +- `esm1-6/`: Small ACCESS-ESM1.5 sample files |
| 115 | +- `om3/`: Ocean model test data |
| 116 | +- `small/`: Minimal test datasets |
| 117 | +- `fixtures/`: Fixed test configurations |
| 118 | + |
| 119 | +### Adding Test Data |
| 120 | + |
| 121 | +When adding new test data: |
| 122 | +1. Keep files small (< 10MB if possible) |
| 123 | +2. Use representative but minimal datasets |
| 124 | +3. Document the source and contents |
| 125 | +4. Use `pytest.mark.skipif` for optional data files |
| 126 | + |
| 127 | +## 🏗️ Writing Tests |
| 128 | + |
| 129 | +### Test Naming Convention |
| 130 | +- Test files: `test_*.py` |
| 131 | +- Test functions: `test_*` |
| 132 | +- Test classes: `Test*` |
| 133 | + |
| 134 | +### Best Practices |
| 135 | + |
| 136 | +1. **Use appropriate test categories**: Mark tests with `@pytest.mark.unit`, `@pytest.mark.integration`, etc. |
| 137 | + |
| 138 | +2. **Use fixtures for setup**: Leverage fixtures in `conftest.py` for common setup. |
| 139 | + |
| 140 | +3. **Make tests independent**: Each test should be able to run in isolation. |
| 141 | + |
| 142 | +4. **Use descriptive names**: Test names should clearly indicate what is being tested. |
| 143 | + |
| 144 | +5. **Test error conditions**: Include tests for error handling and edge cases. |
| 145 | + |
| 146 | +6. **Use subtests for parameterized tests**: When testing multiple similar cases. |
| 147 | + |
| 148 | +### Example Test Structure |
| 149 | + |
| 150 | +```python |
| 151 | +import pytest |
| 152 | +from access_mopper import ACCESS_ESM_CMORiser |
| 153 | + |
| 154 | +class TestNewFeature: |
| 155 | + """Tests for new feature functionality.""" |
| 156 | + |
| 157 | + @pytest.mark.unit |
| 158 | + def test_basic_functionality(self, mock_config): |
| 159 | + """Test basic feature works correctly.""" |
| 160 | + # Test implementation |
| 161 | + |
| 162 | + @pytest.mark.integration |
| 163 | + @pytest.mark.skipif(not_data_available, reason="Test data not available") |
| 164 | + def test_with_real_data(self, parent_experiment_config): |
| 165 | + """Test feature with real data files.""" |
| 166 | + # Test implementation |
| 167 | + |
| 168 | + @pytest.mark.slow |
| 169 | + def test_performance_characteristics(self): |
| 170 | + """Test that feature meets performance requirements.""" |
| 171 | + # Performance test implementation |
| 172 | +``` |
| 173 | + |
| 174 | +## 🐛 Debugging Tests |
| 175 | + |
| 176 | +### Running specific tests |
| 177 | +```bash |
| 178 | +# Run single test |
| 179 | +pytest tests/unit/test_base.py::TestBaseCMORiser::test_init_with_valid_params |
| 180 | + |
| 181 | +# Run with verbose output |
| 182 | +pytest tests/ -v |
| 183 | + |
| 184 | +# Stop on first failure |
| 185 | +pytest tests/ -x |
| 186 | + |
| 187 | +# Drop into debugger on failure |
| 188 | +pytest tests/ --pdb |
| 189 | +``` |
| 190 | + |
| 191 | +### Test output |
| 192 | +- Use `pytest.skip()` for tests that should be skipped |
| 193 | +- Use `pytest.xfail()` for known failures |
| 194 | +- Use `pytest.fail()` with descriptive messages |
| 195 | + |
| 196 | +## 🔄 Migration from Legacy Tests |
| 197 | + |
| 198 | +The original `test_mop.py` has been restructured: |
| 199 | + |
| 200 | +**Old structure**: |
| 201 | +- All tests in single file |
| 202 | +- Duplicate parametrized tests |
| 203 | +- Mixed concerns (smoke + integration + validation) |
| 204 | + |
| 205 | +**New structure**: |
| 206 | +- `test_smoke.py`: Basic import and initialization tests |
| 207 | +- `tests/integration/test_full_cmorisation.py`: Comprehensive CMOR tests |
| 208 | +- Proper separation of unit, integration, and e2e tests |
| 209 | + |
| 210 | +### Backward Compatibility |
| 211 | + |
| 212 | +The old `test_mop.py` file is maintained with a deprecation notice to ensure existing workflows continue to work. |
| 213 | + |
| 214 | +## 🚀 CI/CD Integration |
| 215 | + |
| 216 | +Suggested CI pipeline: |
| 217 | + |
| 218 | +1. **Pull Request**: Run unit tests + smoke tests |
| 219 | +2. **Merge to main**: Run unit + integration tests |
| 220 | +3. **Release**: Run full test suite including e2e and performance tests |
| 221 | + |
| 222 | +Example GitHub Actions workflow: |
| 223 | +```yaml |
| 224 | +- name: Run fast tests |
| 225 | + run: pytest tests/ -m "not slow" |
| 226 | + |
| 227 | +- name: Run slow tests |
| 228 | + run: pytest tests/ -m "slow" |
| 229 | + if: github.event_name == 'push' && github.ref == 'refs/heads/main' |
| 230 | +``` |
0 commit comments