mirror of
https://github.com/aserper/masto-rss.git
synced 2025-12-17 05:15:25 +00:00
- Refactor code into testable bot.py module with MastodonRSSBot class - Create 20+ unit tests covering core functionality and edge cases - Create 10+ integration tests for RSS parsing and Mastodon posting - Add GitHub Actions workflow for automated testing - Unit tests on Python 3.10, 3.11, 3.12 - Integration tests with mocked external services - Code quality checks (flake8, black, mypy) - Docker build validation - Configure pytest with 80% minimum coverage requirement - Add test dependencies in requirements-test.txt - Update .gitignore to exclude test artifacts - Add comprehensive TESTING.md documentation - Add test status badge to README - Maintain full backward compatibility with existing setup
236 lines
6.1 KiB
Markdown
236 lines
6.1 KiB
Markdown
# Testing Guide for Masto-RSS
|
|
|
|
This document describes the testing strategy and how to run tests for the Masto-RSS bot.
|
|
|
|
## Test Architecture
|
|
|
|
The test suite is organized into two main categories:
|
|
|
|
### Unit Tests ([test_bot.py](test_bot.py))
|
|
- Test individual functions and methods in isolation
|
|
- Use mocks and stubs for external dependencies
|
|
- Fast execution time
|
|
- High code coverage
|
|
- Test edge cases and error handling
|
|
|
|
### Integration Tests ([test_integration.py](test_integration.py))
|
|
- Test interactions between components
|
|
- Mock external services (RSS feeds, Mastodon API)
|
|
- Test end-to-end workflows
|
|
- Verify data persistence
|
|
- Test error recovery
|
|
|
|
## Running Tests Locally
|
|
|
|
### Prerequisites
|
|
|
|
```bash
|
|
# Install test dependencies
|
|
pip install -r requirements-test.txt
|
|
```
|
|
|
|
### Run All Tests
|
|
|
|
```bash
|
|
# Run all tests with coverage
|
|
pytest
|
|
|
|
# Run with verbose output
|
|
pytest -v
|
|
|
|
# Run with coverage report
|
|
pytest --cov=bot --cov=main --cov-report=html
|
|
```
|
|
|
|
### Run Specific Test Categories
|
|
|
|
```bash
|
|
# Run only unit tests
|
|
pytest test_bot.py
|
|
|
|
# Run only integration tests
|
|
pytest test_integration.py
|
|
|
|
# Run tests matching a pattern
|
|
pytest -k "test_parse_feed"
|
|
```
|
|
|
|
### Run with Markers
|
|
|
|
```bash
|
|
# Run only unit tests (using markers)
|
|
pytest -m unit
|
|
|
|
# Run only integration tests
|
|
pytest -m integration
|
|
|
|
# Skip slow tests
|
|
pytest -m "not slow"
|
|
```
|
|
|
|
### Coverage Reports
|
|
|
|
```bash
|
|
# Generate HTML coverage report
|
|
pytest --cov=bot --cov=main --cov-report=html
|
|
|
|
# View report
|
|
open htmlcov/index.html # macOS
|
|
xdg-open htmlcov/index.html # Linux
|
|
```
|
|
|
|
## GitHub Actions CI/CD
|
|
|
|
Tests run automatically on every push to `main` and on all pull requests via [.github/workflows/test.yml](.github/workflows/test.yml).
|
|
|
|
### Test Jobs
|
|
|
|
1. **Unit Tests**
|
|
- Runs on Python 3.10, 3.11, 3.12
|
|
- Executes all unit tests
|
|
- Uploads coverage to Codecov
|
|
|
|
2. **Integration Tests**
|
|
- Runs on Python 3.10, 3.11, 3.12
|
|
- Executes all integration tests with mocked external services
|
|
- Uploads coverage to Codecov
|
|
|
|
3. **Code Quality**
|
|
- Runs flake8 for linting
|
|
- Runs black for code formatting checks
|
|
- Runs mypy for type checking
|
|
|
|
4. **Docker Build Test**
|
|
- Builds the Docker image
|
|
- Verifies Python and dependencies are installed
|
|
- Ensures the image can run
|
|
|
|
5. **All Tests Pass**
|
|
- Final job that requires all previous jobs to succeed
|
|
- Provides a single status check for PR requirements
|
|
|
|
## Test Coverage Requirements
|
|
|
|
- **Minimum coverage**: 80%
|
|
- Coverage is measured for `bot.py` and `main.py`
|
|
- Test files are excluded from coverage metrics
|
|
|
|
## Code Quality Standards
|
|
|
|
### Flake8
|
|
- Maximum line length: 127 characters
|
|
- Maximum cyclomatic complexity: 10
|
|
- Critical error codes checked: E9, F63, F7, F82
|
|
|
|
### Black
|
|
- Line length: 88 characters (default)
|
|
- All Python files must pass black formatting
|
|
|
|
### Mypy
|
|
- Type hints encouraged but not required
|
|
- Runs in non-strict mode with missing imports ignored
|
|
|
|
## Test Data and Fixtures
|
|
|
|
### Mock RSS Feeds
|
|
Integration tests use realistic RSS 2.0 and Atom feed XML for testing feed parsing.
|
|
|
|
### Mock Mastodon API
|
|
The Mastodon API is mocked using `unittest.mock` to avoid making real API calls.
|
|
|
|
### Temporary State Files
|
|
Tests use `tempfile.mktemp()` to create temporary state files that are cleaned up after each test.
|
|
|
|
## Writing New Tests
|
|
|
|
### Unit Test Template
|
|
|
|
```python
|
|
import unittest
|
|
from unittest.mock import Mock, patch
|
|
from bot import MastodonRSSBot
|
|
|
|
class TestNewFeature(unittest.TestCase):
|
|
def setUp(self):
|
|
"""Set up test fixtures"""
|
|
self.bot = MastodonRSSBot(
|
|
client_id='test',
|
|
client_secret='test',
|
|
access_token='test',
|
|
instance_url='https://test.com',
|
|
feed_url='https://feed.test/rss.xml',
|
|
state_file='/tmp/test_state.txt'
|
|
)
|
|
|
|
def test_feature(self):
|
|
"""Test description"""
|
|
result = self.bot.some_method()
|
|
self.assertEqual(result, expected_value)
|
|
```
|
|
|
|
### Integration Test Template
|
|
|
|
```python
|
|
import unittest
|
|
import responses
|
|
from bot import MastodonRSSBot
|
|
|
|
class TestNewIntegration(unittest.TestCase):
|
|
@responses.activate
|
|
@patch('bot.Mastodon')
|
|
def test_integration(self, mock_mastodon):
|
|
"""Test description"""
|
|
# Mock HTTP responses
|
|
responses.add(
|
|
responses.GET,
|
|
'https://example.com/feed.xml',
|
|
body=rss_xml,
|
|
status=200
|
|
)
|
|
|
|
# Run test
|
|
bot = MastodonRSSBot(...)
|
|
result = bot.process_new_entries()
|
|
|
|
self.assertEqual(result, expected)
|
|
```
|
|
|
|
## Continuous Integration Status
|
|
|
|
[](https://github.com/aserper/masto-rss/actions/workflows/test.yml)
|
|
|
|
## Troubleshooting
|
|
|
|
### Tests Fail Locally But Pass in CI
|
|
- Ensure you're using the same Python version
|
|
- Check that all dependencies are installed: `pip install -r requirements-test.txt`
|
|
- Clear pytest cache: `pytest --cache-clear`
|
|
|
|
### Coverage Below 80%
|
|
- Identify untested code: `pytest --cov=bot --cov-report=term-missing`
|
|
- Add tests for the missing lines
|
|
- Some error handling paths may be acceptable to skip
|
|
|
|
### Import Errors
|
|
- Ensure the project root is in PYTHONPATH
|
|
- Run tests from the project root directory
|
|
- Check virtual environment is activated
|
|
|
|
## Best Practices
|
|
|
|
1. **Test One Thing**: Each test should verify one specific behavior
|
|
2. **Clear Names**: Test names should describe what they're testing
|
|
3. **Arrange-Act-Assert**: Structure tests with setup, execution, and verification
|
|
4. **Mock External Services**: Never make real HTTP requests or API calls
|
|
5. **Clean Up**: Always clean up temporary files and state
|
|
6. **Test Edge Cases**: Test both happy paths and error conditions
|
|
7. **Keep Tests Fast**: Unit tests should run in milliseconds
|
|
8. **Document Complex Tests**: Add comments explaining non-obvious test logic
|
|
|
|
## Resources
|
|
|
|
- [pytest documentation](https://docs.pytest.org/)
|
|
- [unittest.mock documentation](https://docs.python.org/3/library/unittest.mock.html)
|
|
- [responses library](https://github.com/getsentry/responses)
|
|
- [Coverage.py documentation](https://coverage.readthedocs.io/)
|