{"id":16431133,"url":"https://github.com/rexebin/pulumitesthelper","last_synced_at":"2026-05-13T13:34:41.449Z","repository":{"id":220738072,"uuid":"752468458","full_name":"rexebin/PulumiTestHelper","owner":"rexebin","description":null,"archived":false,"fork":false,"pushed_at":"2024-02-24T22:19:22.000Z","size":764,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-02-08T03:51:12.321Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rexebin.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-02-03T23:30:41.000Z","updated_at":"2024-02-03T23:32:56.000Z","dependencies_parsed_at":"2024-02-04T00:26:33.558Z","dependency_job_id":"ba12cec2-94c3-49b7-a017-dd7e79d3f784","html_url":"https://github.com/rexebin/PulumiTestHelper","commit_stats":null,"previous_names":["rexebin/pulumitesthelper"],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rexebin%2FPulumiTestHelper","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rexebin%2FPulumiTestHelper/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rexebin%2FPulumiTestHelper/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rexebin%2FPulumiTestHelper/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rexebin","download_url":"https://codeload.github.com/rexebin/PulumiTestHelper/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":240645260,"owners_count":19834395,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-11T08:29:16.448Z","updated_at":"2026-05-13T13:34:41.391Z","avatar_url":"https://github.com/rexebin.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"﻿\n# Pulumi Test Helper\n\nThis is a helper package for testing Pulumi programs.\n\nThe goal of this package is to make testing feel like first-class citizen in Pulumi.\n\n# Motivation\n\nThe build-in testing support in Pulumi has the following pain points:\n1. to add a mock, we have to figure out the string representations of the resource identifiers\n2. the mock replaces the property as a whole, potentially removing child properties that are needed for the test\n3. mocking stack reference is not straightforward\n4. a lot of setup code is needed to create a mock\n5. only outputs of resources can be tested \n\nThis package aims to solve these problems by providing a simple and easy to use API for mocking resources, calls and stack references.\n\nIt also provide a way to test the raw inputs of resources.\n\n# Usage\n\n## Build the stack\n\nTwo build methods are provided: \n```c# \npublic async Task\u003cStackResult\u003e BuildStackAsync\u003cT\u003e(TestOptions? testOptions = null) where T : Stack, new()\n    \npublic async Task\u003cStackResult\u003e BuildStackAsync\u003cT\u003e(IServiceProvider serviceProvider,\n        TestOptions? testOptions = null) where T : Stack, new()\n```\n#### Example:\n```c#\nvar (resources, resourceInputs) = await new StackBuilder().BuildStackAsync\u003cMyStack\u003e();\nvar (resources, resourceInputs) = await new StackBuilder().BuildStackAsync\u003cMyStack\u003e(serviceProvider);\n```\n\n## Add Mocks for all resources\n`AddMocksForAllResources` is provided to add mocks for all resources in the stack.\n\nIt takes `MockResourceArgs` as input and returns a dictionary of the resource properties, so that you can provide mocks based on existing resource inputs.\n\n```c#\npublic StackBuilder AddMocksForAllResources(Func\u003cMockResourceArgs, Dictionary\u003cstring, object\u003e\u003e mocks)\n```\n\n#### Example\nBelow tests shows that `arn` property of all resources is mocked with the resource name appended with `_arn`.\n\n```c#\n[Fact]\npublic async Task Should_Mock_All_Resource_Properties_With_Given_Rule()\n{\n    var result = await new StackBuilder().BuildStackAsync\u003cAwsStack\u003e();\n\n    var repository = result.Resources.OfType\u003cRepository\u003e().Single();\n    repository.Arn.GetValue().Should().BeNull();\n\n    result = await new StackBuilder().AddMocksForAllResources(args =\u003e new Dictionary\u003cstring, object\u003e\n    {\n        {\"arn\", $\"{args.Name}_arn\"}\n    }).BuildStackAsync\u003cAwsStack\u003e();\n\n    var resources = result.Resources;\n\n    repository = resources.OfType\u003cRepository\u003e().Single();\n    repository.Arn.GetValue().Should().Be(\"my-repository_arn\");\n\n    var bucket = resources.OfType\u003cBucket\u003e().Single();\n    bucket.Arn.GetValue().Should().Be(\"my-bucket_arn\");\n}\n\n```\n\n## Mock resources\nFour methods are provided to add mocks for resources:\n```c#\nAddResourceMock(ResourceMock resourceMock)\nAddResourceMocks(List\u003cResourceMock\u003e resourceMocks)\nAddResourceMockFunc(ResourceMockFunc resourceMockFunc)\nAddResourceMockFuncs(List\u003cResourceMockFunc\u003e resourceMockFuncs)\n```\n\n#### Example:\n```c#\nvar result = await new StackBuilder()\n        .AddResourceMock(new ResourceMock(typeof(Image),\n            new Dictionary\u003cstring, object\u003e\n            {\n                {\"imageUri\", imageUri}\n            }))\n        .BuildStackAsync\u003cMyStack\u003e();\n\nimage = result.Resources.OfType\u003cImage\u003e().Single(x =\u003e x.HasName(\"my-image\"));\nimage.ImageUri.GetValue().Should().Be(imageUri);\n```\n\n`AddResourceMockFunc` takes the pulumi `MockResourceArgs` as input and returns a dictionary of the resource properties, so that you can provide mocks based on existing resource inputs.\n\n```c#\nvar result = await new StackBuilder()\n    .AddResourceMockFunc(\n        new ResourceMockFunc(typeof(Image),\n                    args =\u003e new Dictionary\u003cstring, object\u003e\n                    {\n                        {\"imageUri\", $\"{args.Name}-{imageUri}\"}\n                    })\n        )\n        .BuildStackAsync\u003cMyStack\u003e();\n```\n\n## Mock calls\nThree methods are provided to add mocks for calls:\n```c#\nAddCallMock(CallMock callMock)\nAddCallMocks(List\u003cCallMock\u003e callMocks)\nAddCallMockFunc(CallMockFunc callMockFunc)\n```\n#### Example:\n\nBelow code makes a call to `GetRepository` and assigns the `RepositoryUrl` value to the `RepositoryUrl` property of the stack as stack output.\n```c#\n[Output] public Output\u003cstring\u003e RepositoryUrl { get; set; }\npublic MyStack()\n{\n    var invoke = GetRepository.Invoke(new GetRepositoryInvokeArgs\n    {\n        Name = \"my-remote-repository\"\n    });\n\n    RepositoryUrl = invoke.Apply(x =\u003e x.RepositoryUrl);\n}\n```\nTo test the above:\n```c#\nvar result = await new StackBuilder()\n    .AddCallMock(new CallMock(typeof(GetRepository),\n        new Dictionary\u003cstring, object\u003e\n        {\n            {\"repositoryUrl\", mock}\n        })).BuildStackAsync\u003cMyStack\u003e();\nvar stack = result.Resources.OfType\u003cMyStack\u003e().Single();\nstack.RepositoryUrl.GetValue().Should().Be(mock);\n```\n\n## Mock stack references\n\nTo mock stack references, use `AddStackReferenceMock` method.\n\n#### Example:\n\nBelow code uses a stack reference to get the `hosted-zone-id` output from another stack.\n```c#\npublic class CoreStackReference\n{\n    public readonly Output\u003cstring\u003e HostedZoneId;\n\n    public CoreStackReference()\n    {\n        var coreStackReference = new StackReference(\"core\");\n        HostedZoneId = coreStackReference.RequireOutput(\"hosted-zone-id\").Apply(x =\u003e x.ToString())!;\n    }\n}\n```\n\n```c#\nvar coreStackReference = new CoreStackReference();\n\n_ = new Bucket(\"my-bucket\", new BucketArgs\n{\n    BucketName = \"my-bucket\",\n    HostedZoneId = coreStackReference.HostedZoneId\n});\n```\nTo test the above:\n```c#\n[Fact]\npublic async Task Should_Add_StackReference_Mock()\n{\n    var noStackReferenceMock = () =\u003e new StackBuilder().BuildStackAsync\u003cAwsStack\u003e();\n    await noStackReferenceMock.Should().ThrowAsync\u003cException\u003e()\n        .WithMessage(\"*Required output 'hosted-zone-id' does not exist on stack 'core'*\");\n\n    var result = await new StackBuilder()\n        .AddStackReferenceMock(new StackReferenceMock(\"core\", new Dictionary\u003cstring, object\u003e\n        {\n            {\"hosted-zone-id\", \"hosted-zone-id-mock\"}\n        })).BuildStackAsync\u003cAwsStack\u003e();\n    var bucket = result.Resources.OfType\u003cBucket\u003e().Single();\n    bucket.HostedZoneId.GetValue().Should().Be(\"hosted-zone-id-mock\");\n}\n```\n## Test raw inputs\nThe resource list from Pulumi `Deployment.TestAsync` (which is used by `BuildStackAsync`) containers list of resources specified in the stack. \n\nHowever, the properties of the resources only includes the Pulumi outputs of the resources, many of the raw properties are absent.\n\nIn case you want to protect against changes in the inputs of the resources, you can use `ResourceInputs` property of the `StackResult` to test the inputs of the resources.\n\nFor example, `Awsx.Erc.Image` resource only have `imageUri` output, it does not output other properties like `Platform`, `Context` etc. \n```c#:\nvar repository = new Repository(\"my-repository\", new RepositoryArgs\n        {\n            ImageScanningConfiguration = new RepositoryImageScanningConfigurationArgs\n            {\n                ScanOnPush = false\n            },\n            ForceDelete = false,\n            ImageTagMutability = \"MUTABLE\"\n        });\n        \nnew Image(\"my-image\", new ImageArgs\n{\n    Platform = \"linux/amd64\",\n    Context = \"./\",\n    RepositoryUrl = repository.RepositoryUrl\n});\n```\n\nTo test the inputs of the resource `Image`:\n```c#\nvar result = await _baseStackBuilder.AddResourceMock(new ResourceMock(typeof(Repository),\n    new Dictionary\u003cstring, object\u003e\n    {\n        {\"repositoryUrl\", \"my-repository_name\"}\n    })).BuildStackAsync\u003cAwsStack\u003e();\n\nvar inputs = result.ResourceInputs.GetInputs(\"my-image\");\nvar platform = inputs.GetValueOrDefault(\"platform\");\nplatform.Should().Be(\"linux/amd64\");\n        \nvar context = inputs.GetValueOrDefault(\"context\");\ncontext.Should().Be(\"./\");\n\nvar repositoryUrl = inputs.GetValueOrDefault(\"repositoryUrl\");\nrepositoryUrl.Should().Be(\"my-repository_name\");\n```\n\n# Contribution\n\nPlease feel free to contribute to this project. PRs are welcome.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frexebin%2Fpulumitesthelper","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frexebin%2Fpulumitesthelper","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frexebin%2Fpulumitesthelper/lists"}