Phase Groups
Structure your OpenHTF tests with Phase Groups for guaranteed teardown execution, setup/main/teardown patterns, and nested group composition.
Group phases into setup, main, and teardown blocks so cleanup always runs.
A PhaseGroup runs its setup phases, then its main phases, then its teardown phases. When a main phase fails and stops the group, the remaining main phases do not run but the teardown phases still execute, like a try/finally block.
- PhaseGroup
- setup
- power_on
- main
- measure_voltage
- measure_current
- teardown
- power_off
Hardware tests often need guaranteed cleanup: close relays, power down devices, disconnect instruments. A PhaseGroup ensures teardown phases run even if setup or main phases fail, similar to a try/finally block.
Syntax
A PhaseGroup takes three lists of phases: setup, main, and teardown. The rules are:
- Setup phases run first. If any fails, the group is never entered — main and teardown are skipped entirely.
- Main phases run after setup succeeds. If any fails, remaining main phases are skipped.
- Teardown phases run after main, regardless of whether main phases passed or failed. They are guaranteed to execute once setup has succeeded.
import openhtf as htf
def connect_instrument(test):
test.logger.info('Connecting to instrument')
return htf.PhaseResult.CONTINUE
def measure_voltage(test):
test.logger.info('Measuring voltage')
return htf.PhaseResult.CONTINUE
def measure_current(test):
test.logger.info('Measuring current')
return htf.PhaseResult.CONTINUE
def disconnect_instrument(test):
test.logger.info('Disconnecting instrument')
return htf.PhaseResult.CONTINUE
def main():
test = htf.Test(
htf.PhaseGroup(
setup=[connect_instrument],
main=[measure_voltage, measure_current],
teardown=[disconnect_instrument],
)
)
test.execute(lambda: "SN1234")
if __name__ == "__main__":
main()Execution rules
Setup succeedsAll setup → all main → all teardown phases run in order.
Setup failsRemaining setup skipped. Main and teardown are skipped entirely — the group was never entered.
Main failsRemaining main phases are skipped. Teardown still runs because the group was entered during setup.
Teardown failsRemaining teardown phases still run. Teardown errors are logged but do not prevent other teardown phases from executing.
import openhtf as htf
def setup_phase(test):
return htf.PhaseResult.CONTINUE
def failing_main_phase(test):
return htf.PhaseResult.STOP # This fails
def skipped_phase(test):
# This will NOT run because failing_main_phase stopped execution
return htf.PhaseResult.CONTINUE
def teardown_phase(test):
# This ALWAYS runs because setup succeeded
test.logger.info('Cleanup complete')
return htf.PhaseResult.CONTINUE
def main():
test = htf.Test(
htf.PhaseGroup(
setup=[setup_phase],
main=[failing_main_phase, skipped_phase],
teardown=[teardown_phase],
)
)
test.execute(lambda: "SN1234")
if __name__ == "__main__":
main()Shorthand decorators
You can use factory methods to create groups more concisely when you only need teardown or setup.
import openhtf as htf
def cleanup(test):
test.logger.info('Cleaning up')
def measure(test):
return htf.PhaseResult.CONTINUE
def main():
# Wrap main phases with a teardown guarantee
group = htf.PhaseGroup.with_teardown(cleanup)(measure)
test = htf.Test(group)
test.execute(lambda: "SN1234")
if __name__ == "__main__":
main()PhaseGroup.with_teardown(*teardown_phases)decoratorReturns a decorator that wraps main phases with guaranteed teardown.
PhaseGroup.with_setup(*setup_phases)decoratorReturns a decorator that runs setup phases before main phases.
PhaseGroup.with_context(setup, teardown)decoratorReturns a decorator that wraps main phases with both setup and teardown.
Nested groups
Phase groups can be nested inside other groups. Each group independently guarantees its own teardown. If an inner group's main phase fails, the inner teardown runs, then the outer teardown runs.
import openhtf as htf
def outer_main(test):
test.logger.info('Outer main phase')
def inner_main(test):
test.logger.info('Inner main phase')
def inner_teardown(test):
test.logger.info('Inner teardown')
def outer_teardown(test):
test.logger.info('Outer teardown')
def main():
test = htf.Test(
htf.PhaseGroup(
main=[
outer_main,
htf.PhaseGroup.with_teardown(inner_teardown)(inner_main),
],
teardown=[outer_teardown],
)
)
test.execute(lambda: "SN1234")
if __name__ == "__main__":
main()When inner_main fails, execution order is: inner_teardown → outer_teardown. Both teardowns run because both groups were entered.
Phases
Discover how to define and execute test phases using OpenHTF, including phase creation, phase results, and phase options with detailed examples.
Measurements
Learn how to capture and validate data within OpenHTF phases, including boolean, string, numeric, multidimensional, and marginal measurements.