3. Test your composition
In the previous guide, you used an embedded function to create composition logic for your cloud resources. This guide walks through how to create a test plan. Tests allow you to:
- Validate your composition configuration without impacting actual resources in your control planes.
- Catch errors earlier in your workflow
- Verify resource creation
- Validate input parameters
Prerequisites
Make sure you've completed the previous guide and have:
- An Upbound account
- The Up CLI installed
- kubectl installed
- Docker Desktop running
- A project with the basic structure (
upbound.yaml
,apis/
,examples/
) - Provider dependencies added
- An XRD generated from your example
XR
- An embedded function that defines your composition logic
If you missed any of the previous steps, go to the project foundations guide to get started.
Generate a composition function test
Create the test scaffolding:
up test generate storagebucket
This command:
- Creates a test directory in
tests/test-storagebucket
- Generates the basic structure for writing tests
The default testing language is KCL. You can specify --langauage=python
in
the test command
Set up your test imports
Open tests/test-storagebucket/main.k
and replace the scaffolding with the
following configuration:
import models.com.example.platform.v1alpha1 as platformv1alpha1
import models.io.upbound.awsm.s3.v1beta1 as awsms3v1beta1
import models.io.upbound.dev.meta.v1alpha1 as metav1alpha1
# It's a best practice for composition functions to return only fields whose
# values they care about, since they become the owner of any field they
# return. Allow for fields with defaults to be omitted by the composition
# function by clearing them in our expected resources.
_stripDefaults = lambda obj: any -> any {
obj | {
spec.managementPolicies = Undefined
}
}
_items = [
metav1alpha1.CompositionTest{
metadata.name="test-storagebucket"
spec = {
assertResources: [
platformv1alpha1.StorageBucket{
metadata.name: "example"
spec.parameters: {
acl: "public-read"
region: "us-west-1"
versioning: True
}
}
_stripDefaults(awsms3v1beta1.Bucket{
metadata.generateName = "example-bucket"
spec.forProvider: {
region: "us-west-1"
}
})
_stripDefaults(awsms3v1beta1.BucketOwnershipControls{
metadata.generateName = "example-boc"
spec.forProvider: {
region: "us-west-1"
rule: {
objectOwnership: "BucketOwnerPreferred"
}
}
})
_stripDefaults(awsms3v1beta1.BucketPublicAccessBlock{
metadata.generateName = "example-pab"
spec.forProvider: {
blockPublicAcls: False
blockPublicPolicy: False
ignorePublicAcls: False
region: "us-west-1"
restrictPublicBuckets: False
}
})
_stripDefaults(awsms3v1beta1.BucketACL{
metadata.generateName = "example-acl"
spec.forProvider:{
acl: "public-read"
region: "us-west-1"
}
})
_stripDefaults(awsms3v1beta1.BucketServerSideEncryptionConfiguration{
metadata.generateName = "example-encryption"
spec.forProvider: {
region: "us-west-1"
rule: [
{
applyServerSideEncryptionByDefault: {
sseAlgorithm: "AES256"
}
bucketKeyEnabled: True
}
]
}
})
_stripDefaults(awsms3v1beta1.BucketVersioning{
metadata.generateName = "example-versioning"
spec.forProvider: {
region: "us-west-1"
versioningConfiguration: {
status: "Enabled"
}
}
})
]
compositionPath: "apis/storagebuckets/composition.yaml"
xrPath: "examples/storagebucket/example.yaml"
xrdPath: "apis/storagebuckets/definition.yaml"
timeoutSeconds: 120
validate: False
}
}
]
items = _items
Review your test
Import the testing models
import models.com.example.platform.v1alpha1 as platformv1alpha1
import models.io.upbound.awsm.s3.v1beta1 as awsms3v1beta1
import models.io.upbound.dev.meta.v1alpha1 as metav1alpha1
This section imports:
- Your custom StorageBucket XRD
- AWS S3 resource schemas for validation
- Upbound testing framework components
Define your test case
Create the main test structure:
_items = [
metav1alpha1.CompositionTest{
metadata.name="test-storagebucket"
spec= {
assertResources: [
# Test assertions will go here
]
compositionPath: "apis/storagebuckets/composition.yaml"
xrPath: "examples/storagebucket/example.yaml"
xrdPath: "apis/storagebuckets/definition.yaml"
timeoutSeconds: 120
validate: False
}
}
]
This section:
- Identifies your test case
- Sets up the
assertResources
section to define expected outputs - Links to your composition, XR and XRD files
- Defines how long to wait for the test to complete
- Defines Whether to validate against live schemas
Test the input XR
assertResources: [
platformv1alpha1.StorageBucket {
metadata.name: "example"
spec.parameters: {
acl: "public-read"
region: "us-west-1"
versioning: True
}
}
This assertion verifies:
- Your composition receives the user's
XR
- Your control plane processes the
acl
,region
andversioning
parameters - The control plane maintains the resource name and structure
Test the storage resource
For all managed resources, strip away all default fields. This follows the best practice for composition functions to return only fields whose values they care about, as they become the owners of those fields.
_stripDefaults(awsms3v1beta1.Bucket{
metadata.generateName = "example-bucket"
spec.forProvider: {
region: "us-west-1"
}
})
This assertion verifies that:
- The main S3 bucket resource is created
- The bucket uses the expected naming pattern (
example-bucket
) - The bucket uses the user's specified region parameter
Test security configurations
_stripDefaults(awsms3v1beta1.BucketOwnershipControls{
metadata.generateName = "example-boc"
spec.forProvider: {
region: "us-west-1"
rule: {
objectOwnership: "BucketOwnerPreferred"
}
}
})
_stripDefaults(awsms3v1beta1.BucketPublicAccessBlock{
metadata.generateName = "example-pab"
spec.forProvider: {
blockPublicAcls: False
blockPublicPolicy: False
ignorePublicAcls: False
region: "us-west-1"
restrictPublicBuckets: False
}
})
This section tests to verify:
- Proper object ownership configuration
- Correct settings for public bucket access
- Security configuration applied to the bucket
Test access control and encryption
_stripDefaults(awsms3v1beta1.BucketACL{
metadata.generateName = "example-acl"
spec.forProvider:{
acl: "public-read"
region: "us-west-1"
}
})
_stripDefaults(awsms3v1beta1.BucketServerSideEncryptionConfiguration{
metadata.generateName = "example-encryption"
spec.forProvider: {
region: "us-west-1"
rule: [
{
applyServerSideEncryptionByDefault: {
sseAlgorithm: "AES256"
}
bucketKeyEnabled: True
}
]
}
})
These tests ensure the control plane:
- User's access control configuration is applied appropriately
- Enabled encryption by default
- Applies security configurations consistently
Test conditional features
_stripDefaults(awsms3v1beta1.BucketVersioning{
metadata.generateName = "example-versioning"
spec.forProvider: {
region: "us-west-1"
versioningConfiguration: {
status: "Enabled"
}
}
})
This assertion verifies:
- Versioning resource only exists when
versioning: True
- The control plane sets versioning to "Enabled"
- The control plane links versioning to the bucket
Final test structure
Your complete test now:
- Simulates a user's StorageBucket
XR
- Verifies resource creation
- Ensures resources are linked together
- Confirms user inputs flow through to created resources
Run your tests
Next, run your composition tests with the up test run
command.
up test run tests/*
✓ Parsing tests
✓ Checking dependencies
✓ Generating language schemas
✓ Building functions
✓ Checking dependencies
✓ Generating language schemas
✓ Building functions
✓ Building configuration package
✓ Pushing embedded functions to local daemon
✓ Assert test-storagebucket
SUCCESS
SUCCESS Tests Summary:
SUCCESS ------------------
SUCCESS Total Tests Executed: 1
SUCCESS Passed tests: 1
SUCCESS Failed tests: 0
Understanding test results
Your test results return:
- Pass/fail status: Whether each assertion succeeded
- Resource validation: Confirmation for resource creation
- Error details: Specific information about any failures
- Coverage summary: Overview of the assertions
What successful tests prove
Passing tests show:
- Your composition function logic is correct
- User parameters processed properly
- All required resources are created
- Security configurations are applied consistently
- Conditional logic works as expected
Next steps
Now that you have tested your composition:
- Deploy with confidence: Your tests prove the composition works
- Iterate safely: Make changes knowing tests will catch regressions
- Add more test cases: Test different parameter combinations and edge cases
- Integrate with CI/CD: Automate testing as part of your development workflow
In the next guide, you'll deploy your tested composition to a control plane and see your infrastructure API in action.