A Crossplane testing framework that leverages crossplane render and crossplane beta validate commands to test if Compositions render correctly against Claims or XRs, and if the rendered manifests validate against their schemas.
- Version Agnostic: Works with any Crossplane CLI version and supports any Composition and Function implementation
- Local Testing: Runs entirely locally with no running Kubernetes cluster required. Only requires a running Docker daemon for Composition Functions
- Multiple Input Types: Supports both XR (Composite Resource) and Claim inputs
- XR Patching: Apply patches on the inputs
- Template Variables: Dynamic content using Go template syntax
- Hooks Support: Pre-test and post-test shell command execution
- Assertions: Validate rendered resources with declarative assertions (count, existence, field checks)
- Test Chaining: Export testcase outputs as artifacts for use in follow-up tests to better emulate the reconciliation process
- CI/CD Ready: Easy integration into any system or pipeline
To bridge the testing gap between function-level testing and full e2e testing: you can run render and validate locally with real Compositions and Functions, without a live cluster or real resource creation. It was originally developed by Elastic, where it is currently being used for mock/integration testing of a large infrastructure.
- Composition testing: Run
crossplane renderusing various combinations of XRs/Claims, Compositions and Functions, and assert on the output. - Schema validation: Validate various combinations of (mock or production) manifests with CRDs via
crossplane beta validate. - Reconciliation emulation: Chain tests with exported artifacts so later tests consume prior outputs (e.g. observed resources, status).
- Advanced render inputs: Drive render with extra resources, observed resources, additional context, environment configs, in multiple tests run one after the other.
- Upgrades: Validate upgrades of Crossplane itself, providers, functions before or after adoption.
Yes. xprin runs entirely locally in a mock environment. It does not create or modify real resources; it only runs crossplane render and crossplane beta validate on the inputs you provide, so you can safely point at production data.
To extend coverage, by creating as many test cases we need without having to provide testdata for each one of them.
The testsuite files are in YAML
tests:
- name: "Successful test with hooks, validation, and assertions"
patches:
xrd: ../../aws/xrd.yaml
inputs:
xr: ../../aws/xr.yaml
composition: ../../aws/composition.yaml
functions: ../../aws/functions.yaml
observed-resources: ../../aws/observed
crds:
- ../../aws/xrd.yaml
- ../../aws/crossplane.yaml
hooks:
pre-test:
- name: "this is a pre-test hook"
run: echo "hello"
post-test:
- name: "this is a post-test hook"
run: echo "bye"
assertions:
xprin:
- name: "Number of resources"
type: "Count"
value: 3
- name: "SecurityGroup should exist"
type: "Exists"
resource: "SecurityGroup/platform-aws-sg"
diff:
- name: "Full render matches golden (using diff)"
expected: golden_full_render.yamlThe output is similar to `go test`
➜ xprin test simple_test_xprin.yaml -v --show-render --show-validate --show-assertions --show-hooks
=== RUN Successful test with hooks, validation, and assertions
--- PASS: Successful test with hooks, validation, and assertions (1.31s)
Pre-test Hooks:
[✓] this is a pre-test hook
hello
Render:
├── XAWSInfrastructure/platform-aws
├── Cluster/platform-aws-rds
└── SecurityGroup/platform-aws-sg
Validate:
[✓] aws.example.com/v1, Kind=XAWSInfrastructure, platform-aws validated successfully
[✓] rds.aws.upbound.io/v1beta1, Kind=Cluster, platform-aws-rds validated successfully
[✓] ec2.aws.upbound.io/v1beta1, Kind=SecurityGroup, platform-aws-sg validated successfully
Total 3 resources: 0 missing schemas, 3 success cases, 0 failure cases
Assertions:
[✓] Number of resources - found 3 resources (as expected)
[✓] SecurityGroup should exist - resource SecurityGroup/platform-aws-sg found
[✓] Full render matches golden (using diff) - files match
Total: 3 assertions, 3 successful, 0 failed, 0 errors
Post-test Hooks:
[✓] this is a post-test hook
bye
PASS
ok examples/mytests/0_e2e/used_in_readme_xprin.yaml 1.314s
See Getting Started and Examples for more.
Crossplane + πριν, before Crossplane! (backstory)
When xprin runs a test case, it follows this specific sequence:
- Setup - Set up environment, expand paths, collect inputs, and copy them to a temporary directory for patching
- Pre-test Hooks - Execute any pre-test hooks defined in the test case
- Convert Claim to XR (optional) - If using a Claim input, convert it to XR using
xprin-helpers convert-claim-to-xr - Patch XR (optional) - Apply patches (XRD defaults, connection secrets) using
xprin-helpers patch-xr - Crossplane Render - Run
crossplane renderwith the XR, Composition, and Functions - Crossplane Validate (optional) - If CRDs are provided, run
crossplane beta validateon the rendered output - Assertions (optional) - Validate rendered resources using declarative assertions (count, existence, field type/value checks)
- Post-test Hooks - Execute any post-test hooks defined in the test case
- Export Artifacts (optional) - If test case has an
id, copy outputs to artifacts directory for cross-test references
Visual flow (high-level steps):
flowchart TD
A["Setup<br/>Pre-test Hooks"] --> B[Convert Claim to XR]
A --> C[Patch XR]
B --> D[Render]
C --> D
D --> E[Validate]
D --> F[Assertions]
E --> G[Post-test Hooks<br/>Export Artifacts]
F --> G
style A fill:#3b82f6,stroke:#1e40af,stroke-width:2px,color:#fff
style B fill:#f97316,stroke:#c2410c,stroke-width:2px,color:#fff
style C fill:#f97316,stroke:#c2410c,stroke-width:2px,color:#fff
style D fill:#a855f7,stroke:#7e22ce,stroke-width:2px,color:#fff
style E fill:#a855f7,stroke:#7e22ce,stroke-width:2px,color:#fff
style F fill:#10b981,stroke:#047857,stroke-width:2px,color:#fff
style G fill:#3b82f6,stroke:#1e40af,stroke-width:2px,color:#fff
For the full flow with decision points and optional steps, see How It Works.
The flow ensures that:
- Pre-test hooks can set up the environment, patch inputs or validate them before processing
- Claims are automatically converted to XRs for compatibility with
crossplane render - XRs can be enhanced with defaults and connection secrets before rendering
- The rendered output is validated against schemas if CRDs are available
- Assertions validate rendered resources declaratively (count, existence, field checks) after validation
- Post-test hooks can clean up and perform additional validation after assertions
- Test outputs can be exported as artifacts (when
idis set) and referenced by later tests via.Tests.{test-id}template variables for test chaining
- Installation - Install xprin and verify your setup
- Optional Configuration if you need custom subcommands or repositories
- Optional IDE integration for autocompletion and validation in your editor
- Getting Started - Run your first test and learn basic commands
- Examples - Step-by-step examples with real outputs
- Test Suite Specification - Complete reference for all test suite fields and options
- Assertions - Complete guide to declarative resource validation
- How It Works - Deep dive into how xprin works under the hood
# Test Compositions
xprin test <targets>
# Check dependencies and configuration
xprin check
# Show version
xprin versionxprin-helpers: Helper utilities for converting Claims to XRs and patching XRs
- convert-claim-to-xr: Convert Claims to XRs
- patch-xr: Apply patches to XRs
See Installation for detailed requirements. At minimum: Crossplane CLI and Docker daemon.
