Testing OPA Policies

πŸ“– Introduction

Untested policies are a liability. A policy that should block images from untrusted registries but silently passes them due to a Rego logic error is worse than no policy β€” it creates false confidence. I learned this the hard way after deploying a hostNetwork restriction that passed all resources because I had a subtle logic inversion in the rule body.

The good news: OPA has a built-in test framework. Policy unit tests are written in Rego, run with opa test, and integrate naturally into CI/CD pipelines. This article covers everything you need to write thorough, maintainable policy tests.


πŸ§ͺ OPA Test Basics

OPA test files are regular .rego files where every rule starting with test_ is a test case. Tests pass if the rule body evaluates to true (or is non-empty for sets). They fail if the body is false, undefined, or empty.

package k8snolatesttag_test

import rego.v1
import data.k8snolatesttag

# --- Test data ---

pod_with_latest := {
    "review": {
        "object": {
            "spec": {
                "containers": [{"name": "app", "image": "nginx:latest"}]
            }
        }
    }
}

pod_with_pinned := {
    "review": {
        "object": {
            "spec": {
                "containers": [{"name": "app", "image": "nginx:1.25.3"}]
            }
        }
    }
}

# --- Tests ---

test_deny_latest_tag if {
    count(k8snolatesttag.violation) > 0 with input as pod_with_latest
}

test_allow_pinned_tag if {
    count(k8snolatesttag.violation) == 0 with input as pod_with_pinned
}

Key patterns:

  • with input as <value> β€” override input for this evaluation

  • import data.<package> β€” import the policy under test

  • Test file uses the _test suffix by convention (not required)


πŸƒ Running Tests

Sample output:


πŸ—‚οΈ Test Structure Conventions

Structure your policy and test files alongside each other:


πŸ“‹ Writing Comprehensive Tests

A complete test file for the image registry policy:

Good test coverage should include:

  • Happy path β€” allowed resource produces no violations

  • Violation path β€” each violation condition triggers

  • Edge cases β€” empty arrays, missing fields, multiple containers

  • Violation message β€” the message content is correct


πŸ”€ Testing with with for Data

When policies reference data (like approved registries loaded externally), you can override it in tests:


πŸ“Š Code Coverage

Run with --coverage to see which lines are exercised by tests:

Uncovered lines usually indicate missing test cases β€” add tests for the edge case that exercises those lines.


🧰 Conftest: Policy Testing for YAML and JSON Files

conftest is a tool built on OPA that makes it easy to test Kubernetes manifests, Terraform plans, and other config files against Rego policies β€” without writing a Gatekeeper setup. It's particularly useful in CI/CD pipelines.

Directory Structure for Conftest

Conftest looks for policies in the policy/ directory by default.

Writing Conftest-compatible Policies

Conftest uses deny, warn, or violation rules:

Note: with Conftest, input is the YAML/JSON document directly (not wrapped in review.object).

Running Conftest

Output:


πŸ”„ Integration with opa test in a Makefile

A simple Makefile target that validates and tests policies:


🧭 What's Next

The final article in this series brings everything together in a CI/CD pipeline β€” enforcing policies on manifests and Terraform plans before anything reaches the cluster.

Next: Article 08 β€” OPA in CI/CD Pipelines


πŸ“Ž References

Last updated