Testing Guide¶
Running Tests¶
python -m pytest tests/ -v
python -m pytest tests/ --cov=src/bluetooth_sig --cov-report=term-missing
python -m pytest tests/ -k "battery" -v
Writing Characteristic Tests¶
All characteristic tests inherit from CommonCharacteristicTests in
tests/gatt/characteristics/test_characteristic_common.py. The base class
automatically runs UUID verification, parse/decode consistency, length
validation, edge-case handling, and round-trip encode/decode against every
entry in valid_test_data.
Required fixtures¶
Fixture |
What to return |
|---|---|
|
A fresh instance of the characteristic under test |
|
The short UUID string, e.g. |
|
A |
import pytest
from bluetooth_sig.gatt.characteristics.battery_level import BatteryLevelCharacteristic
from tests.gatt.characteristics.test_characteristic_common import CharacteristicTestData, CommonCharacteristicTests
class TestBatteryLevelCharacteristic(CommonCharacteristicTests):
@pytest.fixture
def characteristic(self) -> BatteryLevelCharacteristic:
return BatteryLevelCharacteristic()
@pytest.fixture
def expected_uuid(self) -> str:
return "2A19"
@pytest.fixture
def valid_test_data(self) -> list[CharacteristicTestData]:
return [
CharacteristicTestData(bytearray([0]), 0, "empty battery"),
CharacteristicTestData(bytearray([75]), 75, "75% battery"),
CharacteristicTestData(bytearray([100]), 100, "full battery"),
]
What the base class tests automatically¶
Every valid_test_data entry is exercised by:
test_characteristic_uuid_matches_expectedtest_parse_valid_data_succeedsandtest_decode_valid_data_returns_expected_valuetest_round_trip—parse_value → build_valuemust reproduce the original bytestest_empty_data_handling,test_undersized_data_handling,test_oversized_data_validationtest_length_validation_behaviour,test_range_validation_behaviourtest_parse_decode_consistency,test_decode_exception_handlingtest_uuid_resolution_behaviourDependency coverage enforcement (see below)
When to add explicit test methods¶
Only add a method when it tests something valid_test_data cannot express:
Structural byte assertions — checking a specific offset or flag bit in the encoded output.
Error conditions — inputs that must raise an exception.
Dependency scenarios — use the
dependency_test_datafixture (see below).
Do not add separate encode or round-trip methods — those are covered automatically.
Dependencies¶
If the characteristic declares required_dependencies or optional_dependencies,
the base class enforces a dependency_test_data fixture. See
test_characteristic_common.py for the DependencyTestData structure and
tests/gatt/characteristics/test_characteristic_dependencies.py for examples.
Test Structure¶
tests/
├── conftest.py
├── gatt/
│ └── characteristics/
│ ├── test_characteristic_common.py ← base class and shared helpers
│ ├── test_characteristic_dependencies.py
│ └── test_<characteristic_name>.py ← one file per characteristic
├── advertising/
├── core/
├── registry/
├── integration/
└── ...
Continuous Integration¶
- name: Run tests
run: pytest tests/ --cov=src/bluetooth_sig --cov-report=xml
See .github/workflows/ for the full CI configuration.