Skip to main content

Overview

Testing policies is critical to ensure your authorization logic works as expected. Cerbos provides a comprehensive testing framework that lets you:
  • Validate policy syntax: Catch errors before deployment
  • Verify authorization decisions: Ensure rules produce expected results
  • Test edge cases: Cover deny scenarios, conditions, and derived roles
  • Integrate with CI/CD: Run tests automatically on every commit
Policy tests are written in YAML and run with the cerbos compile command. They don’t require a running Cerbos server.

The cerbos compile Command

Basic Usage

# Compile policies and run all tests
cerbos compile /path/to/policies

# Compile without running tests
cerbos compile --skip-tests /path/to/policies

# Compile with verbose output
cerbos compile --verbose /path/to/policies

What It Does

1

Load Policies

Loads all policy files from the specified directory.
2

Validate Syntax

Checks YAML syntax and policy structure.
3

Compile Policies

Compiles policies into internal representation, checking:
  • Schema references are valid
  • Derived roles are imported correctly
  • Conditions use valid CEL expressions
  • Variables and constants are defined
4

Run Tests

Executes test suites and reports results.

Command Options

--skip-tests
boolean
Skip running tests, only validate and compile policies.
--test-filter
string
Filter tests by dimensions (suite, test, principal, resource, action).Format: dimension=glob1,glob2;...Example: --test-filter='suite=DocumentTests;principal=alice,bob'
--verbose
boolean
Show detailed output on test failures including full request/response.
--output
string
default:"tree"
Output format: tree, list, or json.
--ignore-schemas
boolean
Skip schema validation during compilation.

Test File Structure

Test files are YAML documents that define test suites:
---
name: DocumentTestSuite
description: Tests for document resource policy

principals:
  alice:
    id: alice
    roles: ["user"]
    attr:
      department: engineering
  
  bob:
    id: bob
    roles: ["user", "manager"]
    attr:
      department: sales

resources:
  public_doc:
    kind: document
    id: doc1
    attr:
      owner: alice
      public: true
  
  private_doc:
    kind: document
    id: doc2
    attr:
      owner: alice
      public: false

tests:
  - name: Public documents viewable by all
    input:
      principals: [alice, bob]
      resources: [public_doc]
      actions: [view]
    expected:
      - principal: alice
        resource: public_doc
        actions:
          view: EFFECT_ALLOW
      
      - principal: bob
        resource: public_doc
        actions:
          view: EFFECT_ALLOW

File Location

Test files should be in a tests/ directory within your policy repository:
policies/
├── derived_roles/
│   └── common_roles.yaml
├── resource_policies/
│   ├── document.yaml
│   └── project.yaml
└── tests/
    ├── document_test.yaml
    └── project_test.yaml

Test Suite Fields

Suite Metadata

name
string
required
Unique name for this test suite. Used in test output and filtering.
description
string
Human-readable description of what this suite tests.

Principals Definition

principals
object
required
Map of principal definitions. Keys are principal identifiers used in tests.
principals:
  alice:
    id: alice                    # Principal ID (matches policy checks)
    roles: ["user", "employee"]  # Static roles
    attr:                        # Custom attributes
      department: engineering
      level: 5
      teams: ["backend", "platform"]
    scope: "acme.corp"           # Optional scope
  
  service_account:
    id: backup-service
    roles: ["service"]
    attr:
      service_type: backup

Resources Definition

resources
object
required
Map of resource definitions. Keys are resource identifiers used in tests.
resources:
  alice_document:
    kind: document               # Resource type
    id: doc123                   # Resource ID
    attr:                        # Custom attributes
      owner: alice
      department: engineering
      status: published
      created_at: "2024-01-15T10:00:00Z"
    scope: "acme.corp"           # Optional scope
  
  shared_project:
    kind: project
    id: proj456
    attr:
      owner: bob
      collaborators: ["alice", "charlie"]
      public: true

Tests Definition

tests
array
required
List of test cases to execute.
Each test specifies:
tests:
  - name: "Test case name"       # Required: descriptive name
    skip: false                  # Optional: skip this test
    skipReason: "Not ready"      # Optional: why test is skipped
    
    input:                       # What to test
      principals: [alice, bob]   # List of principal IDs
      resources: [doc1, doc2]    # List of resource IDs
      actions: [view, edit]      # List of actions
      auxData:                   # Optional: additional data
        jwt:
          aud: "my-app"
    
    expected:                    # Expected results
      - principal: alice         # For this principal
        resource: doc1           # And this resource
        actions:                 # Expect these results
          view: EFFECT_ALLOW
          edit: EFFECT_ALLOW

Complete Test Example

Here’s a comprehensive test suite for a leave request system:
---
name: LeaveRequestTestSuite
description: |
  Tests for leave request resource policy.
  Covers employee self-service, manager approval, and admin access.

principals:
  alice:
    id: alice
    roles: ["employee"]
    attr:
      department: engineering
      manager: bob
      geography: US
  
  bob:
    id: bob
    roles: ["employee", "manager"]
    attr:
      department: engineering
      managed_users: ["alice", "charlie"]
      geography: US
  
  charlie:
    id: charlie
    roles: ["employee"]
    attr:
      department: sales
      manager: dave
      geography: UK
  
  admin:
    id: admin
    roles: ["admin"]

resources:
  alice_pending_request:
    kind: leave_request
    id: req1
    attr:
      owner: alice
      status: PENDING_APPROVAL
      department: engineering
      geography: US
      days: 5
  
  alice_approved_request:
    kind: leave_request
    id: req2
    attr:
      owner: alice
      status: APPROVED
      department: engineering
      geography: US
      days: 3
  
  charlie_request:
    kind: leave_request
    id: req3
    attr:
      owner: charlie
      status: PENDING_APPROVAL
      department: sales
      geography: UK
      days: 10

tests:
  # Employee can create their own requests
  - name: Employees can create leave requests
    input:
      principals: [alice, charlie]
      resources: [alice_pending_request, charlie_request]
      actions: [create]
    expected:
      - principal: alice
        resource: alice_pending_request
        actions:
          create: EFFECT_ALLOW
      
      - principal: charlie
        resource: charlie_request
        actions:
          create: EFFECT_ALLOW
  
  # Employee can view their own requests
  - name: Employees can view their own requests
    input:
      principals: [alice]
      resources: [alice_pending_request, alice_approved_request]
      actions: [view]
    expected:
      - principal: alice
        resource: alice_pending_request
        actions:
          view: EFFECT_ALLOW
      
      - principal: alice
        resource: alice_approved_request
        actions:
          view: EFFECT_ALLOW
  
  # Employee cannot view others' requests
  - name: Employees cannot view other employees requests
    input:
      principals: [alice]
      resources: [charlie_request]
      actions: [view]
    expected:
      - principal: alice
        resource: charlie_request
        actions:
          view: EFFECT_DENY
  
  # Manager can approve their reports' requests
  - name: Managers can approve pending requests from their reports
    input:
      principals: [bob]
      resources: [alice_pending_request]
      actions: [approve]
    expected:
      - principal: bob
        resource: alice_pending_request
        actions:
          approve: EFFECT_ALLOW
  
  # Manager cannot approve already approved requests
  - name: Managers cannot approve already-approved requests
    input:
      principals: [bob]
      resources: [alice_approved_request]
      actions: [approve]
    expected:
      - principal: bob
        resource: alice_approved_request
        actions:
          approve: EFFECT_DENY
  
  # Manager cannot approve requests from other departments
  - name: Managers cannot approve requests outside their geography
    input:
      principals: [bob]
      resources: [charlie_request]
      actions: [approve]
    expected:
      - principal: bob
        resource: charlie_request
        actions:
          approve: EFFECT_DENY
  
  # Admin has full access
  - name: Admins have full access to all requests
    input:
      principals: [admin]
      resources: [alice_pending_request, charlie_request]
      actions: [view, edit, approve, delete]
    expected:
      - principal: admin
        resource: alice_pending_request
        actions:
          view: EFFECT_ALLOW
          edit: EFFECT_ALLOW
          approve: EFFECT_ALLOW
          delete: EFFECT_ALLOW
      
      - principal: admin
        resource: charlie_request
        actions:
          view: EFFECT_ALLOW
          edit: EFFECT_ALLOW
          approve: EFFECT_ALLOW
          delete: EFFECT_ALLOW

Running Tests

Run All Tests

cerbos compile /path/to/policies
Output:
Loading policies from /path/to/policies
Compiling 15 policies...
✓ All policies compiled successfully

Running tests...
LeaveRequestTestSuite
  ✓ Employees can create leave requests
  ✓ Employees can view their own requests
  ✓ Employees cannot view other employees requests
  ✓ Managers can approve pending requests from their reports
  ✓ Managers cannot approve already-approved requests
  ✓ Managers cannot approve requests outside their geography
  ✓ Admins have full access to all requests

7 passed, 0 failed

Run Specific Tests

Filter by suite name:
cerbos compile --test-filter='suite=LeaveRequestTestSuite' /path/to/policies
Filter by test name pattern:
cerbos compile --test-filter='test=*Manager*' /path/to/policies
Filter by principal:
cerbos compile --test-filter='principal=alice,bob' /path/to/policies
Filter by resource:
cerbos compile --test-filter='resource=*_request' /path/to/policies
Filter by action:
cerbos compile --test-filter='action=approve,delete' /path/to/policies
Combine filters:
cerbos compile --test-filter='suite=Leave*;principal=alice;action=view' /path/to/policies

Verbose Output

See detailed failure information:
cerbos compile --verbose /path/to/policies
Failure output shows:
  • Expected vs actual effects
  • Full request details
  • Which policies were evaluated
  • Derived roles that were activated

Testing Patterns

Test Positive Cases

tests:
  - name: Owner can edit their documents
    input:
      principals: [alice]
      resources: [alice_doc]
      actions: [edit]
    expected:
      - principal: alice
        resource: alice_doc
        actions:
          edit: EFFECT_ALLOW

Test Negative Cases

tests:
  - name: Users cannot edit others' documents
    input:
      principals: [bob]
      resources: [alice_doc]
      actions: [edit]
    expected:
      - principal: bob
        resource: alice_doc
        actions:
          edit: EFFECT_DENY

Test Multiple Principals

tests:
  - name: All users can view public documents
    input:
      principals: [alice, bob, charlie]  # Test multiple principals at once
      resources: [public_doc]
      actions: [view]
    expected:
      - principal: alice
        resource: public_doc
        actions:
          view: EFFECT_ALLOW
      
      - principal: bob
        resource: public_doc
        actions:
          view: EFFECT_ALLOW
      
      - principal: charlie
        resource: public_doc
        actions:
          view: EFFECT_ALLOW

Test Multiple Actions

tests:
  - name: Admins have full access
    input:
      principals: [admin]
      resources: [document]
      actions: [view, edit, delete, share]  # Test all actions
    expected:
      - principal: admin
        resource: document
        actions:
          view: EFFECT_ALLOW
          edit: EFFECT_ALLOW
          delete: EFFECT_ALLOW
          share: EFFECT_ALLOW

Test Conditions

resources:
  fresh_doc:
    kind: document
    id: doc1
    attr:
      created_at: "2024-01-15T10:00:00Z"  # Recent
  
  old_doc:
    kind: document
    id: doc2
    attr:
      created_at: "2020-01-15T10:00:00Z"  # Old

tests:
  - name: Can only edit documents created in last 30 days
    input:
      principals: [alice]
      resources: [fresh_doc, old_doc]
      actions: [edit]
    expected:
      - principal: alice
        resource: fresh_doc
        actions:
          edit: EFFECT_ALLOW  # Recent document
      
      - principal: alice
        resource: old_doc
        actions:
          edit: EFFECT_DENY   # Too old

Test with Auxiliary Data

tests:
  - name: JWT authentication required for sensitive actions
    input:
      principals: [alice]
      resources: [sensitive_doc]
      actions: [delete]
      auxData:
        jwt:
          aud: "admin-app"
          roles: ["admin"]
    expected:
      - principal: alice
        resource: sensitive_doc
        actions:
          delete: EFFECT_ALLOW

Skipping Tests

Temporarily disable tests:
tests:
  - name: Feature not implemented yet
    skip: true
    skipReason: "Waiting for backend implementation"
    input:
      principals: [alice]
      resources: [doc]
      actions: [archive]
    expected:
      - principal: alice
        resource: doc
        actions:
          archive: EFFECT_ALLOW

CI/CD Integration

GitHub Actions

name: Test Policies

on:
  push:
    branches: [main]
  pull_request:
    paths:
      - 'policies/**'
      - 'tests/**'

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      
      - name: Download Cerbos
        run: |
          curl -L https://github.com/cerbos/cerbos/releases/latest/download/cerbos_Linux_x86_64.tar.gz | tar xz
          sudo mv cerbos /usr/local/bin/
      
      - name: Compile and test policies
        run: cerbos compile --verbose policies/

GitLab CI

test-policies:
  image: ghcr.io/cerbos/cerbos:latest
  script:
    - cerbos compile --verbose policies/
  only:
    changes:
      - policies/**
      - tests/**

Pre-commit Hook

#!/bin/bash
# .git/hooks/pre-commit

echo "Testing Cerbos policies..."
cerbos compile policies/

if [ $? -ne 0 ]; then
  echo "Policy tests failed. Commit aborted."
  exit 1
fi

Best Practices

Test Both Allow and Deny

For every ALLOW test, write a corresponding DENY test to ensure policies aren’t too permissive.

Cover Edge Cases

Test boundary conditions, missing attributes, empty lists, and null values.

Use Descriptive Names

Test names should clearly describe what’s being tested and the expected outcome.

One Concept Per Test

Each test should verify one specific behavior. Don’t combine unrelated checks.

Test Derived Roles

Verify that derived roles are assigned correctly by testing actions that depend on them.

Keep Tests DRY

Reuse principal and resource definitions across tests in the same suite.

Troubleshooting

  • Check that resource attributes match your policy conditions exactly
  • Verify principal roles are correct
  • Look for wildcard rules (* in roles or actions)
  • Check if a principal policy is overriding the resource policy
  • Verify the principal has the required static or derived roles
  • Check that conditions evaluate to true with the test data
  • Ensure derived roles are imported in the resource policy
  • Check for explicit DENY rules that might be matching
  • Run cerbos compile --verbose to see detailed error messages
  • Check that referenced derived roles exist and are imported
  • Verify CEL expressions are syntactically correct
  • Ensure variables and constants are defined before use
  • Ensure test files are in the tests/ directory
  • Check that test files have .yaml extension
  • Verify test file structure matches the schema
  • Use --verbose to see which test files are loaded

Test Output Formats

Tree Format (Default)

cerbos compile --output=tree /path/to/policies
Shows hierarchical test results:
LeaveRequestTestSuite
  ✓ Employees can create leave requests
  ✓ Managers can approve
  ✗ Failed test name
    Expected: EFFECT_ALLOW
    Got: EFFECT_DENY

List Format

cerbos compile --output=list /path/to/policies
Flat list of results:
✓ LeaveRequestTestSuite > Employees can create leave requests
✓ LeaveRequestTestSuite > Managers can approve
✗ LeaveRequestTestSuite > Failed test name

JSON Format

cerbos compile --output=json /path/to/policies
Machine-readable JSON output for integration with other tools.

JUnit Format

cerbos compile --test-output=junit /path/to/policies > results.xml
Generates JUnit XML for CI/CD integrations.

What to Test

1

Happy Path

Verify that authorized users can perform expected actions.
2

Unauthorized Access

Verify that unauthorized users are denied access.
3

Role-Based Access

Test that each role has appropriate permissions.
4

Derived Roles

Verify dynamic role assignment based on context.
5

Conditions

Test attribute-based rules with various attribute values.
6

Edge Cases

Test boundary conditions, missing data, and special scenarios.
7

Principal Policies

Verify that principal policies correctly override resource policies.

Example Test Suite

Here’s a minimal but comprehensive test suite template:
---
name: MyResourceTestSuite
description: Tests for my_resource policy

principals:
  regular_user:
    id: user1
    roles: ["user"]
  
  admin_user:
    id: admin1
    roles: ["admin"]

resources:
  user_owned:
    kind: my_resource
    id: res1
    attr:
      owner: user1
  
  other_owned:
    kind: my_resource
    id: res2
    attr:
      owner: user2

tests:
  # Positive: Users can access their own resources
  - name: Users can view their own resources
    input:
      principals: [regular_user]
      resources: [user_owned]
      actions: [view]
    expected:
      - principal: regular_user
        resource: user_owned
        actions:
          view: EFFECT_ALLOW
  
  # Negative: Users cannot access others' resources
  - name: Users cannot view others resources
    input:
      principals: [regular_user]
      resources: [other_owned]
      actions: [view]
    expected:
      - principal: regular_user
        resource: other_owned
        actions:
          view: EFFECT_DENY
  
  # Admin: Admins can access all resources
  - name: Admins can access all resources
    input:
      principals: [admin_user]
      resources: [user_owned, other_owned]
      actions: [view, edit, delete]
    expected:
      - principal: admin_user
        resource: user_owned
        actions:
          view: EFFECT_ALLOW
          edit: EFFECT_ALLOW
          delete: EFFECT_ALLOW
      
      - principal: admin_user
        resource: other_owned
        actions:
          view: EFFECT_ALLOW
          edit: EFFECT_ALLOW
          delete: EFFECT_ALLOW

Next Steps

Resource Policies

Learn how to write policies to test

Conditions

Test complex conditional logic

CI/CD Integration

Automate policy testing in your pipeline

Build docs developers (and LLMs) love