QA Engineer Skills 2026QA-2026Pulumi Testing

Pulumi Testing

Why Pulumi Changes the Testing Game

Pulumi's fundamental advantage is that infrastructure is defined in real programming languages -- TypeScript, Python, Go, or C#. This means you can use the same testing frameworks, assertion libraries, and CI patterns you already know. There is no need to learn a new DSL or testing tool: if you know pytest, you can test Pulumi Python programs. If you know vitest, you can test Pulumi TypeScript programs.

This is a significant departure from Terraform's HCL, where testing requires either specialized tools (Terratest, written in Go) or JSON plan analysis. With Pulumi, the test is just code testing code.


Unit Testing with Pulumi Mocks

Pulumi's mock framework lets you test infrastructure logic without deploying anything. The mocks intercept all cloud API calls and return predictable responses.

TypeScript Unit Tests

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
import { describe, it, expect, beforeAll } from "vitest";

// Mock all Pulumi resource creation
pulumi.runtime.setMocks({
    newResource: (args: pulumi.runtime.MockResourceArgs) => {
        // Return mock values for each resource type
        switch (args.type) {
            case "aws:s3/bucket:Bucket":
                return {
                    id: `${args.name}-id`,
                    state: {
                        ...args.inputs,
                        arn: `arn:aws:s3:::${args.inputs.bucket || args.name}`,
                        bucket: args.inputs.bucket || args.name,
                    },
                };
            case "aws:rds/instance:Instance":
                return {
                    id: `${args.name}-id`,
                    state: {
                        ...args.inputs,
                        endpoint: "mock-db.cluster-abc123.us-east-1.rds.amazonaws.com",
                    },
                };
            default:
                return {
                    id: `${args.name}-id`,
                    state: args.inputs,
                };
        }
    },
    call: (args: pulumi.runtime.MockCallArgs) => {
        return args.inputs;
    },
});

describe("S3 Data Bucket", () => {
    it("must have encryption enabled", async () => {
        const bucket = new aws.s3.Bucket("test-bucket", {
            serverSideEncryptionConfiguration: {
                rule: {
                    applyServerSideEncryptionByDefault: {
                        sseAlgorithm: "aws:kms",
                    },
                },
            },
        });

        const encryption = await new Promise((resolve) =>
            bucket.serverSideEncryptionConfiguration.apply((config) =>
                resolve(config?.rule?.applyServerSideEncryptionByDefault?.sseAlgorithm)
            )
        );
        expect(encryption).toBe("aws:kms");
    });

    it("must block public access", async () => {
        const bucket = new aws.s3.Bucket("test-bucket", {});
        const publicAccessBlock = new aws.s3.BucketPublicAccessBlock("test-pab", {
            bucket: bucket.id,
            blockPublicAcls: true,
            blockPublicPolicy: true,
            ignorePublicAcls: true,
            restrictPublicBuckets: true,
        });

        const blockAcls = await new Promise((resolve) =>
            publicAccessBlock.blockPublicAcls.apply(resolve)
        );
        expect(blockAcls).toBe(true);
    });

    it("must have versioning enabled", async () => {
        const bucket = new aws.s3.Bucket("test-bucket", {
            versioning: { enabled: true },
        });

        const versioning = await new Promise((resolve) =>
            bucket.versioning.apply((v) => resolve(v?.enabled))
        );
        expect(versioning).toBe(true);
    });
});

Python Unit Tests

# tests/test_infrastructure.py
import pulumi
import pytest

class MockResource:
    """Mock Pulumi resource provider for unit testing."""

    def __init__(self):
        self.resources = {}

    def __call__(self, args):
        self.resources[args.name] = args.inputs
        return pulumi.runtime.MockResourceArgs(
            id_=f"{args.name}-id",
            state=args.inputs,
        )

@pytest.fixture
def mock_pulumi():
    mock = MockResource()
    pulumi.runtime.set_mocks(
        pulumi.runtime.Mocks(
            new_resource=mock,
            call=lambda args: {},
        )
    )
    return mock

def test_database_not_publicly_accessible(mock_pulumi):
    """RDS instances must never be publicly accessible."""
    import pulumi_aws as aws

    db = aws.rds.Instance(
        "test-db",
        instance_class="db.t4g.micro",
        allocated_storage=20,
        engine="postgres",
        publicly_accessible=False,
    )

    # Assert the publicly_accessible property
    def check(value):
        assert value is False, "Database must not be publicly accessible"

    db.publicly_accessible.apply(check)

def test_rds_has_encryption(mock_pulumi):
    """RDS instances must have storage encryption enabled."""
    import pulumi_aws as aws

    db = aws.rds.Instance(
        "test-db",
        instance_class="db.t4g.micro",
        allocated_storage=20,
        engine="postgres",
        storage_encrypted=True,
    )

    def check(value):
        assert value is True, "Database storage must be encrypted"

    db.storage_encrypted.apply(check)

Policy Tests with Pulumi CrossGuard

Pulumi CrossGuard is Pulumi's policy-as-code framework. Unlike external tools like Checkov or tfsec that analyze HCL, CrossGuard policies are written in the same language as your infrastructure and run at deployment time.

TypeScript Policy Pack

// policy/index.ts
import { PolicyPack, validateResourceOfType } from "@pulumi/policy";
import * as aws from "@pulumi/aws";

new PolicyPack("security-policies", {
    policies: [
        {
            name: "s3-no-public-read",
            description: "S3 buckets must not allow public read access.",
            enforcementLevel: "mandatory",
            validateResource: validateResourceOfType(aws.s3.Bucket, (bucket, args, reportViolation) => {
                if (bucket.acl === "public-read" || bucket.acl === "public-read-write") {
                    reportViolation("S3 bucket has public read access. Use 'private' ACL.");
                }
            }),
        },
        {
            name: "rds-encryption-required",
            description: "RDS instances must have storage encryption enabled.",
            enforcementLevel: "mandatory",
            validateResource: validateResourceOfType(aws.rds.Instance, (db, args, reportViolation) => {
                if (!db.storageEncrypted) {
                    reportViolation("RDS instance must have storageEncrypted set to true.");
                }
            }),
        },
        {
            name: "ec2-no-public-ip",
            description: "EC2 instances must not have public IP addresses in production.",
            enforcementLevel: "advisory",
            validateResource: validateResourceOfType(aws.ec2.Instance, (instance, args, reportViolation) => {
                if (instance.associatePublicIpAddress) {
                    reportViolation("EC2 instance should not have a public IP. Use a load balancer.");
                }
            }),
        },
        {
            name: "required-tags",
            description: "All taggable resources must have required tags.",
            enforcementLevel: "mandatory",
            validateResource: (args, reportViolation) => {
                const tags = (args.props as any).tags;
                const requiredTags = ["Environment", "Team", "CostCenter"];

                if (tags) {
                    for (const required of requiredTags) {
                        if (!tags[required]) {
                            reportViolation(`Missing required tag: ${required}`);
                        }
                    }
                }
            },
        },
    ],
});

Running CrossGuard Policies

# Run policies against a Pulumi preview (dry run)
pulumi preview --policy-pack ./policy/

# Run policies on actual deployment
pulumi up --policy-pack ./policy/

# Publish policies to Pulumi Cloud for organization-wide enforcement
pulumi policy publish ./policy/

Integration Testing with Pulumi Automation API

The Pulumi Automation API allows you to drive Pulumi from within test code, similar to how Terratest drives Terraform:

// tests/integration/s3-bucket.test.ts
import { LocalWorkspace, Stack } from "@pulumi/pulumi/automation";
import { S3Client, GetBucketEncryptionCommand } from "@aws-sdk/client-s3";
import { describe, it, expect, afterAll } from "vitest";

describe("S3 Data Bucket Integration Test", () => {
    let stack: Stack;
    let bucketName: string;

    it("deploys and validates the bucket", async () => {
        // Create a stack using inline Pulumi program
        stack = await LocalWorkspace.createOrSelectStack({
            stackName: "test-integration",
            projectName: "s3-test",
            program: async () => {
                const aws = await import("@pulumi/aws");
                const bucket = new aws.s3.Bucket("test-data-bucket", {
                    serverSideEncryptionConfiguration: {
                        rule: {
                            applyServerSideEncryptionByDefault: {
                                sseAlgorithm: "aws:kms",
                            },
                        },
                    },
                    versioning: { enabled: true },
                });
                return { bucketName: bucket.bucket };
            },
        });

        // Deploy real infrastructure
        const upResult = await stack.up({ onOutput: console.log });
        bucketName = upResult.outputs.bucketName.value;

        // Verify with AWS SDK
        const s3 = new S3Client({ region: "us-east-1" });
        const encryption = await s3.send(
            new GetBucketEncryptionCommand({ Bucket: bucketName })
        );

        expect(
            encryption.ServerSideEncryptionConfiguration?.Rules?.[0]
                ?.ApplyServerSideEncryptionByDefault?.SSEAlgorithm
        ).toBe("aws:kms");
    }, 120_000); // 2-minute timeout for real infra

    afterAll(async () => {
        if (stack) {
            await stack.destroy({ onOutput: console.log });
            await stack.workspace.removeStack("test-integration");
        }
    });
});

Pulumi vs Terraform Testing Comparison

Aspect Terraform Pulumi
Unit test language Go (Terratest) or Python (plan JSON) Same language as infra (TS, Python, Go, C#)
Mock support Limited (plan analysis) Built-in mock framework
Policy framework External (OPA, Checkov, tfsec) Built-in (CrossGuard)
Integration tests Terratest (Go only) Automation API (any language)
Test speed (unit) Fast (plan analysis) Very fast (mocks, no cloud)
Test speed (integration) Slow (real infra) Slow (real infra)
Learning curve Moderate (learn Go for Terratest) Low (use your existing language)
Community tooling Extensive (Terratest, Checkov, tfsec) Growing (CrossGuard, native tests)

Which to Choose

  • If your team already uses Terraform and Go, Terratest is mature and well-documented.
  • If your team uses TypeScript or Python, Pulumi's native testing is significantly lower friction.
  • If you need organization-wide policy enforcement, Pulumi CrossGuard offers a more integrated experience than bolting OPA onto Terraform.
  • Regardless of choice, the testing principle is the same: static checks at the base, plan/preview analysis in the middle, real infrastructure at the top.