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 assertResourcessection 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,regionandversioningparameters
- 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.