Unit-testing rules with promtool

Prometheus · Advanced · 12 min

Alerting rules are code that pages people; a test file catches a bad threshold or a broken template before it deploys.

promtool can check syntax (promtool check rules file.yml, promtool check config prometheus.yml) and, more valuably, execute rules against fake data with promtool test rules tests.yml.

A test file names the rule_files to load, an evaluation_interval, and a list of tests. Each test has:

  • interval: spacing of the input samples.
  • input_series: series written in a compact notation. '0+10x5' means start at 0 and add 10 five times (0 10 20 30 40 50); '1 1 0 0' lists values; _ is a missing sample and stale a stale marker. Counters are just increasing sequences; a drop simulates a reset.
  • alert_rule_test: at eval_time, which alerts of alertname are expected (exp_alerts) with their exp_labels and exp_annotations. An empty exp_alerts asserts the alert is not firing, which is the test most people forget.
  • promql_expr_test: evaluate any expression at eval_time and compare with exp_samples; use it for recording rules and tricky expressions.

Time starts at 0 and eval_time is relative to it, so a for: 5m alert fires at eval_time: 5m only if the condition held since minute 0. Pending alerts are not reported.

exp_labels must list all labels of the alert, including those from the series and the rule, and annotations are compared after templating, which is how you test humanize output and runbook links.

Run the tests in CI alongside promtool check rules, and promtool check config with --syntax-only when the referenced files aren't present on the build machine.

Example

# tests/api.test.yml   ->   promtool test rules tests/api.test.yml
rule_files:
  - ../rules/api.yml
evaluation_interval: 1m
tests:
  - interval: 1m
    input_series:
      - series: 'http_requests_total{job="api",instance="a",status="200"}'
        values: '0+100x20'          # 100 req/min, steady
      - series: 'http_requests_total{job="api",instance="a",status="500"}'
        values: '0+10x20'           # 10 errors/min = 9% error ratio
    promql_expr_test:
      - expr: job:http_error_ratio:rate5m
        eval_time: 10m
        exp_samples:
          - labels: 'job:http_error_ratio:rate5m{job="api"}'
            value: 0.09090909090909091
    alert_rule_test:
      - eval_time: 9m               # for: 10m not satisfied yet
        alertname: HighErrorRatio
        exp_alerts: []
      - eval_time: 16m
        alertname: HighErrorRatio
        exp_alerts:
          - exp_labels: {job: api, severity: page}
            exp_annotations:
              summary: "api error ratio is 9.091%"
# Unit Testing:  tests/api.test.yml
#   SUCCESS

Remember

  • promtool test rules runs rules on synthetic input_series
  • 'a+bxn' notation; _ gaps and stale markers
  • Assert both firing and not-firing, with all labels and rendered annotations

Watch out for

  • Forgetting that eval_time counts from 0, so a for: 10m alert tested at 5m 'mysteriously' doesn't fire.

Check yourself

  1. How many samples does values: '0+5x3' produce, and what are they?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: prometheus.io/docs/prometheus/latest/configuration/unit_testing_rules/

Builds on

Unlocks

Nothing builds on this yet.