This guide shows you how to add tests to your package. You’ll learn how to write test files, use inline inputs, and run the test harness.
Test file structure
Section titled “Test file structure”Place test files in the tests directory of your package:
Directoryacme/
Directorytests/
- normalize.input Sample data for the test
- normalize.tql Test file
- normalize.txt Expected output baseline
Directorycontext/
- test.yaml Suite configuration
- 01-update.tql First test in suite
- 01-update.txt
- 02-inspect.tql Second test in suite
- 02-inspect.txt
Each test consists of:
- A
.tqlfile containing the test pipeline - An optional
.inputfile with test-specific data - A
.txtfile with the expected output baseline
Use inline inputs
Section titled “Use inline inputs”Inline inputs are the preferred way to provide test data. Place a .input
file next to your test file with the same base name:
Directorytests/
- parse-csv.input Input data
- parse-csv.tql Test file
- parse-csv.txt Expected baseline
The harness exposes the input file path via TENZIR_INPUT:
from_file env("TENZIR_INPUT")read_csvname,valueAlice,42Bob,17Inline inputs keep test data next to the test that uses it, making tests self-contained and easy to understand.
When to use shared inputs
Section titled “When to use shared inputs”For data shared across multiple tests in a subdirectory, create a local
inputs/ directory. The harness uses the nearest inputs/ directory when
resolving TENZIR_INPUTS:
Directorytests/
Directorynetwork/
Directoryinputs/ Shared by tests in network/ and children
- packets.pcap
- flows.json
Directorytcp/
- analysis.tql TENZIR_INPUTS → ../inputs/
Directoryudp/
- stats.tql TENZIR_INPUTS → ../inputs/
Directoryinputs/ Fallback for tests without a closer inputs/
- common.json
Access shared inputs in TQL:
from_file f"{env("TENZIR_INPUTS")}/packets.pcap"acme::analyzePlace inputs/ directories as close to the tests that use them as possible.
This keeps related data together and makes it clear which tests depend on which
files. Prefer inline .input files for single-test data and local inputs/
directories for data shared within a test group.
Write test pipelines
Section titled “Write test pipelines”Test pipelines exercise your package logic with known input and produce deterministic output. The most common pattern is testing user-defined operators (UDOs), which are the primary way to build reusable building blocks. However, you can test any TQL code, including standalone pipelines or complex workflows.
Test an operator
Section titled “Test an operator”from_file env("TENZIR_INPUT")acme::normalize{"@timestamp": "2024-01-15T10:30:00Z", "msg": "test"}Test with different arguments
Section titled “Test with different arguments”Create separate test files for different argument combinations:
from {hash: "abc123"}acme::tag indicatorfrom {hash: "abc123"}acme::tag indicator, prefix="IOC: "Test error conditions
Section titled “Test error conditions”Use the error frontmatter to expect non-zero exit codes:
---error: true---
from {invalid: null}acme::strict_parseRun tests
Section titled “Run tests”Run tenzir-test from the package root (where package.yaml lives) or from the
tests/ subdirectory. The harness auto-detects package mode and configures
paths accordingly.
Preview output in passthrough mode
Section titled “Preview output in passthrough mode”First, run tests in passthrough mode to see the actual output:
uvx tenzir-test --passthroughThis streams output directly to the terminal without comparing against baselines.
Update baselines
Section titled “Update baselines”When the output looks correct, save it as the baseline:
uvx tenzir-test --updateThis creates or updates .txt files next to each test. For example,
tests/normalize.tql produces tests/normalize.txt.
Compare against baselines
Section titled “Compare against baselines”Run all tests and compare against saved baselines:
uvx tenzir-testThe harness reports differences between actual output and baselines. Use
--verbose for detailed output during debugging.
Run specific tests
Section titled “Run specific tests”Target individual tests or directories:
uvx tenzir-test tests/normalize.tqluvx tenzir-test tests/context/Test frontmatter options
Section titled “Test frontmatter options”Control test behavior with YAML frontmatter:
---timeout: 60---
// Long-running test pipeline| Option | Type | Default | Description |
|---|---|---|---|
timeout | integer | 30 | Command timeout in seconds |
error | boolean | false | Expect non-zero exit code |
skip | string | unset | Skip test with reason |
fixtures | list | [] | Fixtures to request |
runner | string | by suffix | Runner name (tenzir, python, shell) |
Troubleshooting
Section titled “Troubleshooting”Test fails with “file not found”
Section titled “Test fails with “file not found””Verify the .input file exists next to the test file with the same base name.
Check that you’re using env("TENZIR_INPUT") (singular) for inline inputs.
Context not found
Section titled “Context not found”Ensure the test suite has fixtures: [node] in test.yaml. The node fixture
automatically installs the package, creating defined contexts.
Non-deterministic output
Section titled “Non-deterministic output”Tests must produce deterministic output. Use sort to order results, and avoid
timestamps or random values in output. For time-based tests, use fixed input
data rather than now().
Baseline mismatch after changes
Section titled “Baseline mismatch after changes”Run uvx tenzir-test --update to regenerate baselines after intentional
changes. Review the diff to verify the changes are expected.