# opa test: Unit Test Rego Policies in CI

> opa test runs Rego unit tests (rules prefixed test_) and reports pass/fail. Reference for -v, --coverage, --threshold, and the test failure output in CI.

Source: https://latchkey.dev/learn/command-reference/opa-test-rego  
Updated: 2026-06-30

opa test discovers Rego rules whose names start with test_ and evaluates them, failing the run if any test is false or errors.

Policies are code and deserve tests. opa test executes every test_ rule under the paths you give it, so you catch a broken deny rule before it blocks a real deployment.

## What it does

opa test loads the paths you pass, finds rules named test_* , and evaluates each one. A test passes if it evaluates to true and fails if it is false, undefined, or raises an error. It prints a summary and exits non-zero on any failure.

## Common usage

```Terminal
# run all tests under the policy directory
opa test policy/ -v
# with code coverage and a minimum threshold
opa test policy/ --coverage --threshold 80
# run a single file plus its test file
opa test policy/deny.rego policy/deny_test.rego -v
```

## Options

| Flag | What it does |
| --- | --- |
| -v, --verbose | Show output for passing tests too |
| --coverage | Report per-file coverage of the policy code |
| --threshold <n> | Fail if coverage is below this percentage |
| -r, --run <regex> | Only run tests whose name matches the regex |
| --bench | Benchmark the tests instead of just running them |
| -f, --format <fmt> | Output format: pretty, json, gobench |

## In CI

Run opa test as a required check on every policy PR, and add --coverage --threshold to stop untested rules from merging. Test files conventionally live next to the policy as <name>_test.rego. The JSON format (-f json) is easy to archive as a test report.

## Common errors in CI

"FAIL: data.main.test_deny_missing_label" with "FAIL (0s)" in the summary means that assertion evaluated to false. "ERROR: ... rego_type_error" means the test itself has a type bug. "no test cases found" (an empty run that still exits 0) usually means the path is wrong or the rules are not prefixed test_. A coverage run below --threshold exits 2.

## Using this in CI

A runner has no kubeconfig, no cached context, and no interactive auth. Every kubectl invocation in CI needs the context supplied explicitly, and most confusing CI failures here are the command running against the wrong cluster or no cluster at all.

```Terminal
# never rely on the ambient context on a runner
kubectl --context "$KUBE_CONTEXT" -n "$NAMESPACE" get pods

# confirm what you are actually connected to before mutating anything
kubectl config current-context
kubectl cluster-info

# fail fast instead of hanging on an unreachable API server
kubectl --request-timeout=30s get nodes
```

> Always set `--request-timeout` in CI. Without it an unreachable API server hangs until the job times out, which turns a thirty-second failure into a twenty-minute one.

## FAQ

### opa test: Unit Test Rego Policies in CI?

Policies are code and deserve tests. opa test executes every test_ rule under the paths you give it, so you catch a broken deny rule before it blocks a real deployment.

### What it does?

opa test loads the paths you pass, finds rules named test_* , and evaluates each one. A test passes if it evaluates to true and fails if it is false, undefined, or raises an error. It prints a summary and exits non-zero on any failure.

### In CI?

Run opa test as a required check on every policy PR, and add --coverage --threshold to stop untested rules from merging. Test files conventionally live next to the policy as <name>_test.rego. The JSON format (-f json) is easy to archive as a test report.

### Common errors in CI?

"FAIL: data.main.test_deny_missing_label" with "FAIL (0s)" in the summary means that assertion evaluated to false. "ERROR: ... rego_type_error" means the test itself has a type bug. "no test cases found" (an empty run that still exits 0) usually means the path is wrong or the rules are not prefixed test_.

---

Latchkey runs CI/CD that repairs its own failures. Agent entry points: https://latchkey.dev/agent.txt, https://latchkey.dev/openapi.json, https://latchkey.dev/llms.txt
