budtestlibrary
Write test cases with budtestlibrary, check their Bloom metadata, and tune what results capture.
A universal test framework with lifecycle management, rich assertions, logging, and Bloom integration: covering HIL, SIL, Web, Mobile, Cloud, and E2E testing. Subclass BudTestCase and write your checks as bud_* methods. Optionally attach BloomMetaData for traceability.
budtestlibrary on PyPI · budtestlibrary on GitHub
pip install budtestlibraryA test case
Section titled “A test case”BudTestCase.run() discovers bud_* methods in alphabetical order. Use numeric prefixes such as bud_01_connect and bud_02_measure when order matters. After execution, get_results() returns the method results, including each method’s name and pass status.
import loggingfrom budtestlibrary import BudTestCase, BloomMetaData
class TraceableTest(BudTestCase): bloom_metadata = BloomMetaData("PRJ", "001") # optional Bloom traceability
def setUpClass(self): self.log_info("Setting up test...")
def bud_check_response(self): response = get_response() self.assertTrue(response.ok, msg="Response is successful")
def bud_validate_output(self): self.assertInTolerance(compute(), expected=42.0, absolute_tolerance=0.5)
def tearDownClass(self): self.log_info("Tearing down test...")
if __name__ == "__main__": test = TraceableTest() test.set_loglevel(logging.INFO) test.run()To run test cases through Bud, list them in a module attribute and pass it to bud_runner run-tests. See Getting started.
Check Bloom metadata
Section titled “Check Bloom metadata”Check the test case IDs a suite claims before uploading results. This command imports test modules, reports duplicate IDs, missing metadata, and import errors, and exits non-zero on duplicates. Use --strict to also fail on missing metadata, --list to show the ID-to-class map, or --json for a machine-readable report.
budtestlibrary check tests/Use trusted test modules: importing them can execute Python code. This check does not run the test suite or contact Bud or Bloom, so it cannot confirm that an ID exists in Bloom.
To send outcomes for these IDs to Bloom, see Connecting to Bloom.
Assertions
Section titled “Assertions”assertTrue / assertFalse, assertEqual / assertNotEqual, assertGreater / assertLess, assertIn / assertNotIn, assertRegex, and skipAssert, plus two numeric checks built for measurement:
self.assertInTolerance(actual, expected, absolute_tolerance=0.1, # ±0.1 relative_tolerance=0.05, # ±5% msg="within range")
self.assertInRange(actual, lower_bound=0.0, upper_bound=10.0, include_bounds=True, msg="within range")Results and capture
Section titled “Results and capture”Each bud_* method produces a TestMethodResult (method name, pass/skip, assertions, duration_seconds, error and summary messages). Each assertion produces a TestResult with expected, actual, source location, and traceback. bud_runner flattens these into the payload it uploads to Bud TMP. Tune capture with class attributes:
class CompactResultsTest(BudTestCase): CAPTURE_SOURCE_PATH = False # omit source file/line/function CAPTURE_TRACEBACK = False # omit tracebacks MAX_RESULT_VALUE_LENGTH = 500 # truncate long expected/actual [default: 5000]Firmware flashing
Section titled “Firmware flashing”For firmware workflows, model programming outcomes with FlashEvent, FlashSuccess, and FlashFailure. FlashEvent.execute() takes a firmware_path:
class MyFlashEvent(FlashEvent): def flash(self, firmware_path, addr=None): ... return FlashSuccess()
result = MyFlashEvent().execute("/path/to/firmware.hex")Subclass FlashEvent for product-specific flashing. Both flash() and execute() take an optional addr target memory address: 0x08000000 on STM32, 0x10000 on ESP32, and None means the implementation’s own default.
from budtestlibrary import FlashEvent, FlashSuccess
class MyFlashEvent(FlashEvent): DEFAULT_ADDR = 0x08000000
def flash(self, firmware_path, addr=None): perform_flash(firmware_path, self.DEFAULT_ADDR if addr is None else addr) return FlashSuccess(message="Flashed successfully")
def get_project_name(self): return "Demo"
event = MyFlashEvent()event.execute("firmware.bin") # implementation defaultevent.execute("bootloader.bin", addr=0x08000000) # explicit addressImplementations that need no address may keep the single-argument flash(self, firmware_path) signature: execute() detects that and calls them unchanged, so existing subclasses keep working. Passing an explicit addr to one of those returns a FlashFailure naming the class and the signature change it needs.
Configuration
Section titled “Configuration”export BUD_BACKEND_URL="https://bud.example.com/"export BUD_TOKEN="your-api-token"# or app.properties: budBackend=... budRunnerAccount=station-01Keep tokens out of app.properties: it is for project metadata only. See Identity and secrets.
Examples
Section titled “Examples”The source distribution ships runnable examples for every domain: minimal_test.py, bloom_metadata_test.py, flash_event_example.py, hil_test.py, sil_test.py, api_testing_example.py, ui_testing_example.py, and cloud_e2e_example.py.



