(Posted by Rik; written by Claude.)
Pattern
unit_test.py runs every registered test against one shared PredBat/HA fixture (create_predbat()), and this keeps producing the same class of bug: a test mutates shared state, restores it incompletely or unconditionally-to-a-default instead of to what was actually there, and leaks into whichever test runs next.
Recent instances, all found by review rather than by the leak actually being caught:
Each was fixed individually as found. The pattern itself isn't tracked anywhere, and there's no mechanism that would catch the next one before a maintainer or reviewer spots it by hand.
Why it keeps happening
- The shared-fixture-across-all-tests design means any test that mutates instance state is a landmine for every test that runs after it, and the blast radius depends on run order.
- "Snapshot before mutating, restore what was actually there" (not
= {} or a hardcoded default) is a convention, not something enforced - easy to get right for the field you're thinking about and miss another one a function touches incidentally.
- Nothing currently detects a leak automatically; it surfaces as an unrelated test failing later, which is confusing to debug and easy to misattribute (see
test_state_fingerprint idea already on Rik's backlog, aimed at exactly this).
Possible directions (not scoped, for discussion)
- A generic snapshot/restore helper other test modules could reuse instead of each writing its own (a couple of ad-hoc versions of this already exist, e.g. in
test_inverter.py).
- A registry-level assertion that fixture state is unchanged after each test (or each module) - would catch a leak at its source test rather than downstream.
- Isolating truly destructive tests (ones that call something like
fetch_rates()/_apply_rates() that touch dozens of fields) onto a throwaway PredBat instance rather than the shared one, since restoring 24 fields by hand doesn't scale.
Filed to track the pattern; no fix attempted here.
(Posted by Rik; written by Claude.)
Pattern
unit_test.pyruns every registered test against one sharedPredBat/HA fixture (create_predbat()), and this keeps producing the same class of bug: a test mutates shared state, restores it incompletely or unconditionally-to-a-default instead of to what was actually there, and leaks into whichever test runs next.Recent instances, all found by review rather than by the leak actually being caught:
compare.fetch_rates()/annual._apply_rates()mutate ~24 fields on the shared fixture (dashboard_values,control_ledger, window lists among them) when driven directly in a test - brokeoptimise_levelswhen tried.argsbut not the HA interface'sdummy_items, leaking 12 dummy entities into every later test in the module - the same gap existed in the pre-existing test Copilot cited as the model to follow.soc_kwh_historyto{}unconditionally instead of restoring whatever an earlier test had left there.PREDBAT_APPS_FILE/args["chat"]mutated for the rest of the process.Each was fixed individually as found. The pattern itself isn't tracked anywhere, and there's no mechanism that would catch the next one before a maintainer or reviewer spots it by hand.
Why it keeps happening
= {}or a hardcoded default) is a convention, not something enforced - easy to get right for the field you're thinking about and miss another one a function touches incidentally.test_state_fingerprintidea already on Rik's backlog, aimed at exactly this).Possible directions (not scoped, for discussion)
test_inverter.py).fetch_rates()/_apply_rates()that touch dozens of fields) onto a throwawayPredBatinstance rather than the shared one, since restoring 24 fields by hand doesn't scale.Filed to track the pattern; no fix attempted here.