Testing Guide#
This guide covers testing practices and guidelines for PhyloZoo.
Setup#
Testing Dependencies#
PhyloZoo uses a small set of testing tools:
pytest >= 7.0.0
pytest-cov >= 4.0.0
You can install them directly:
pip install pytest pytest-cov
or as part of the development extra when working on PhyloZoo itself:
pip install -e ".[dev]"
Test Organization#
Test Framework#
PhyloZoo uses pytest as the testing framework. Tests are located in the tests/ directory
and mirror the source code structure.
Test Structure#
Tests are organized to mirror the source code structure:
tests/core/ - Tests for core modules (networks, quartets, splits, sequences, distance)
tests/viz/ - Tests for visualization
tests/utils/ - Tests for utility modules
tests/conftest.py - Shared fixtures and pytest configuration
Running Tests#
Run all tests:
pytest
Run tests with coverage:
pytest --cov=phylozoo --cov-report=html
Run specific test files:
pytest tests/core/network/test_dnetwork.py
Run tests matching a pattern:
pytest -k "test_split"
Run tests and show print statements:
pytest -s
Run tests in verbose mode:
pytest -v
Writing Tests#
Guidelines#
When adding new tests:
Create a test file named test_<module_name>.py in the appropriate subdirectory
Import the module you’re testing
Use descriptive test function names starting with test_
Use pytest fixtures from conftest.py when appropriate
Add docstrings to test classes and functions explaining what they test
Use type hints for test functions
Example:
def test_my_function() -> None:
"""Test that my_function works correctly."""
result = my_function(input)
assert result == expected_output
Test Classes#
Group related tests in test classes:
class TestMyClass:
"""Test cases for MyClass."""
def test_basic_functionality(self) -> None:
"""Test basic functionality."""
# Test code here
pass
def test_edge_case(self) -> None:
"""Test edge case handling."""
# Test code here
pass
Fixtures#
Shared fixtures are defined in tests/conftest.py. Use fixtures for common test data:
def test_with_fixture(sample_network) -> None:
"""Test using a fixture."""
assert sample_network.num_nodes > 0
Best Practices#
Test both success and failure cases: Test that functions work correctly and handle errors appropriately
Use descriptive assertions: Include clear error messages in assertions when possible
Test edge cases: Test with empty inputs, single elements, boundary conditions
Keep tests independent: Each test should be able to run independently
Use fixtures for common setup: Avoid duplicating test setup code
Continuous Integration#
Every push to master and every pull request against master triggers the
CI GitHub Actions workflow defined in .github/workflows/ci.yml. The workflow
runs three jobs in parallel:
Tests (required) — runs the full
pytestsuite (with coverage) on Python 3.10 and Python 3.11, installing the package with thedevextras.Lint (required) — runs
ruff check src testsandblack --check src tests.Type check (advisory) — runs
mypy src. The job reports the current mypy error count on every PR so progress can be tracked, but is markedcontinue-on-error: trueso it does not block merging while the codebase is being cleaned up. Once the error count reaches zero thecontinue-on-errorflag should be removed and the job promoted to “required”.
The configuration for each tool is taken from pyproject.toml, so running them
locally before pushing produces the same result the workflow does:
pytest --cov=phylozoo --cov-report=term
ruff check src tests
black --check src tests
mypy src
Concurrent runs on the same branch are cancelled automatically, so only the most recent commit in a pull request is checked.
The CI badge in the project README links to the most recent workflow runs on
master.
In addition to ci.yml (which gates merges to master), the
.github/workflows/ directory also contains:
docs.yml— builds and deploys the Sphinx documentation to GitHub Pages on every version tag.release.yml— builds the source distribution and wheel, and publishes them to PyPI on every version tag.