Testing Guide¶
Comprehensive guide for running tests in Amiibot.
๐ Overview¶
Amiibot uses pytest for unit testing with coverage reporting. Tests run automatically on every push to GitHub via GitHub Actions.
Current Coverage¶
Coverage Graph¶
๐ Quick Start¶
# Install dependencies
uv sync --group dev
# Run all tests
uv run pytest
# Run with verbose output
uv run pytest -v
# Run with coverage
uv run pytest --cov
# Run specific test file
uv run pytest tests/test_database.py
# Run specific test
uv run pytest tests/test_database.py::TestDatabase::test_remove_currency_us_format
๐ Test Structure¶
tests/
โโโ __init__.py
โโโ test_config.py # Configuration tests
โโโ test_database.py # Database tests
โโโ test_utils.py # Utility function tests
๐งช Test Categories¶
Unit Tests¶
Test individual functions and methods in isolation: - test_database.py - Database operations, currency parsing, validation - test_utils.py - Utility functions (formatting, calculations, etc.) - test_config.py - Configuration loading and validation
Integration Tests (Future)¶
Test interactions between components: - Scraper โ Database flow - Database โ Messenger flow - Complete scraping cycle
โ๏ธ Running Tests¶
Run All Tests¶
Run with Verbose Output¶
Run with Coverage Report¶
# Terminal report
uv run pytest --cov
# HTML report (opens in browser)
uv run pytest --cov --cov-report=html
open htmlcov/index.html
Run Specific Tests¶
# Run specific file
uv run pytest tests/test_database.py
# Run specific class
uv run pytest tests/test_database.py::TestDatabase
# Run specific test
uv run pytest tests/test_database.py::TestDatabase::test_remove_currency_us_format
# Run tests matching pattern
uv run pytest -k "currency"
Run with Output¶
# Show print statements
uv run pytest -s
# Show local variables on failure
uv run pytest -l
# Stop after first failure
uv run pytest -x
# Run last failed tests
uv run pytest --lf
๐ Coverage Reports¶
Generate Coverage Report¶
# Terminal report with missing lines
uv run pytest --cov --cov-report=term-missing
# HTML report
uv run pytest --cov --cov-report=html
# XML report (for CI/CD)
uv run pytest --cov --cov-report=xml
View HTML Coverage¶
# Generate and open HTML report
uv run pytest --cov --cov-report=html
open htmlcov/index.html # macOS
xdg-open htmlcov/index.html # Linux
start htmlcov/index.html # Windows
Coverage Configuration¶
Coverage settings are in pytest.ini:
- Source: All Python files
- Omit: Tests, venv, site-packages
- Exclude: Abstract methods, debug code
๐ฏ Writing Tests¶
Test Structure¶
import pytest
class TestMyFeature:
"""Test my feature."""
@pytest.fixture
def my_fixture(self):
"""Setup for tests."""
return "test_data"
def test_something(self, my_fixture):
"""Test something."""
assert my_fixture == "test_data"
Using Fixtures¶
@pytest.fixture
def database(self):
"""Create test database."""
config = DatabaseConfig(engine="sqlite", name="test_db")
return Database(config)
def test_with_database(self, database):
"""Test using database fixture."""
assert database is not None
Testing Exceptions¶
def test_invalid_input(self, database):
"""Test invalid input raises ValueError."""
with pytest.raises(ValueError, match="invalid"):
database.remove_currency("invalid")
Parametrized Tests¶
@pytest.mark.parametrize("input,expected", [
("$19.99", 19.99),
("ยฃ1,234.56", 1234.56),
("โฌ10.00", 10.00),
])
def test_currency_parsing(self, database, input, expected):
"""Test various currency formats."""
assert database.remove_currency(input) == expected
๐ค GitHub Actions (CI/CD)¶
Automatic Testing¶
Tests run automatically on: - Push to main/develop - Pull requests - Manual workflow dispatch
Workflow Jobs¶
- Test - Run tests on Python 3.12 and 3.13
- Lint - Run ruff, black, mypy
- Security - Run bandit security scanner
View Results¶
Go to your repository on GitHub: 1. Click Actions tab 2. Select a workflow run 3. View job results and logs
Status Badges¶
Add to README.md:
๐ Debugging Tests¶
Run with Debugging¶
# Drop into debugger on failure
uv run pytest --pdb
# Drop into debugger on error
uv run pytest --pdbcls=IPython.terminal.debugger:Pdb
Print Debug Info¶
# Show print statements
uv run pytest -s
# Show local variables on failure
uv run pytest -l --tb=long
# Extra verbose
uv run pytest -vv
๐ Test Examples¶
Database Test Example¶
def test_update_or_insert_last_scraped(self, database):
"""Test updating last scraped timestamp."""
stockist = "test.com"
# First insert
database.update_or_insert_last_scraped(stockist)
# Verify it was inserted
with database.Session() as session:
record = session.query(LastScraped).filter_by(stockist=stockist).first()
assert record is not None
assert record.stockist == stockist
Utility Function Test Example¶
def test_format_price_thousands(self):
"""Test price formatting with thousands separator."""
assert format_price(1234.56, "$") == "$1,234.56"
assert format_price(12345.67, "ยฃ") == "ยฃ12,345.67"
Configuration Test Example¶
def test_config_validation_empty_stockists(self):
"""Test configuration validation with empty stockists list."""
config_data = {...} # Invalid config
with pytest.raises(ValueError, match="at least one stockist"):
load_config(temp_path)
๐ฏ Test Coverage Goals¶
| Component | Current | Goal |
|---|---|---|
| Database | 80%+ | 90%+ |
| Utils | 90%+ | 95%+ |
| Config | 85%+ | 90%+ |
| Overall | 85%+ | 90%+ |
๐ Continuous Integration¶
Local Pre-commit¶
CI Pipeline¶
On every push: 1. โ Run all tests 2. โ Check code formatting (black) 3. โ Check linting (ruff) 4. โ Check types (mypy) 5. โ Security scan (bandit) 6. โ Generate coverage report
๐ Additional Resources¶
๐ Troubleshooting¶
Tests Not Found¶
# Make sure you're in the right directory
cd /path/to/Amiibot
# Make sure pytest can find tests
uv run pytest --collect-only
Import Errors¶
# Install dependencies
uv sync --group dev
# Check Python path
uv run python -c "import sys; print(sys.path)"
Fixture Not Found¶
โจ Summary¶
- Run tests:
uv run pytest - With coverage:
uv run pytest --cov - Verbose:
uv run pytest -v - Specific test:
uv run pytest tests/test_database.py::test_name - CI/CD: Automatic on push via GitHub Actions