-
Notifications
You must be signed in to change notification settings - Fork 72
Add unit tests to aws-typescript #863
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,9 +1,4 @@ | ||
| import * as pulumi from "@pulumi/pulumi"; | ||
| import * as aws from "@pulumi/aws"; | ||
| import * as awsx from "@pulumi/awsx"; | ||
| import * as infra from "./infra"; | ||
|
|
||
| // Create an AWS resource (S3 Bucket) | ||
| const bucket = new aws.s3.BucketV2("my-bucket"); | ||
|
|
||
| // Export the name of the bucket | ||
| export const bucketName = bucket.id; | ||
| // Export the name of the bucket. | ||
| export const bucketName = infra.bucket.id; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| import * as pulumi from "@pulumi/pulumi"; | ||
| import * as aws from "@pulumi/aws"; | ||
| import * as awsx from "@pulumi/awsx"; | ||
|
|
||
| // Create an AWS resource (S3 Bucket) with tags. | ||
| export const bucket = new aws.s3.BucketV2("my-bucket", { | ||
| tags: { | ||
| "Name": "My bucket", | ||
| }, | ||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| /** @type {import('ts-jest').JestConfigWithTsJest} **/ | ||
| module.exports = { | ||
| testEnvironment: "node", | ||
| transform: { | ||
| "^.+.tsx?$": ["ts-jest", {}], | ||
| }, | ||
| }; |
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,51 @@ | ||||||||||
| import * as pulumi from "@pulumi/pulumi"; | ||||||||||
| import "jest"; | ||||||||||
|
|
||||||||||
| // Test helper to convert a Pulumi Output to a Promise. | ||||||||||
| // This should only be used in tests. | ||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we could elaborate a bit more here why this should only be used in tests, maybe something along the lines of:
Suggested change
|
||||||||||
| function promiseOf<T>(output: pulumi.Output<T>): Promise<T> { | ||||||||||
| return new Promise(resolve => output.apply(resolve)); | ||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I experimented with custom jest matchers here #868 This keeps the "magic" of Outputs, and avoids the (slightly wrong) conversion to promises here.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am not arguing for including these custom matchers in this PR. I was curious to see what it would look like. I think it's something we can follow up with if there are is enough demand to warrant the extra work. We could package the matchers in a library, that can be (optionally) used for writing tests. |
||||||||||
| } | ||||||||||
|
|
||||||||||
| describe("infrastructure", () => { | ||||||||||
| // Define the infra variable as a type whose shape matches the that of the | ||||||||||
| // to-be-defined infra module. | ||||||||||
| let infra: typeof import("../infra"); | ||||||||||
|
|
||||||||||
| beforeAll(() => { | ||||||||||
| // Put Pulumi in unit-test mode, mocking all calls to cloud-provider APIs. | ||||||||||
| pulumi.runtime.setMocks({ | ||||||||||
| // Mock calls to create new resources and return a canned response. | ||||||||||
| newResource: (args: pulumi.runtime.MockResourceArgs) => { | ||||||||||
| // Here, we're returning a same-shaped object for all resource types. | ||||||||||
| // We could, however, use the arguments passed into this function to | ||||||||||
| // customize the mocked-out properties of a particular resource. | ||||||||||
| // See the unit-testing docs for details: | ||||||||||
| // https://www.pulumi.com/docs/iac/concepts/testing/unit/ | ||||||||||
| return { | ||||||||||
| id: `${args.name}-id`, | ||||||||||
| state: args.inputs, | ||||||||||
| }; | ||||||||||
| }, | ||||||||||
|
|
||||||||||
| // Mock function calls and return an empty response. | ||||||||||
| call: (args: pulumi.runtime.MockCallArgs) => { | ||||||||||
| return {}; | ||||||||||
| }, | ||||||||||
| }); | ||||||||||
| }); | ||||||||||
|
|
||||||||||
| beforeEach(async () => { | ||||||||||
| // Dynamically import the infra module. | ||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We could mention that this needs to happen after the mocks have been setup, although it is already captured by the use of
Suggested change
|
||||||||||
| infra = await import("../infra"); | ||||||||||
| }); | ||||||||||
|
|
||||||||||
| // Example test. To run, uncomment and run `npm test`. | ||||||||||
| // describe("bucket", () => { | ||||||||||
| // it("must have a name tag", async () => { | ||||||||||
| // const tags = await promiseOf(infra.bucket.tags); | ||||||||||
| // expect(tags).toBeDefined(); | ||||||||||
| // expect(tags).toHaveProperty("Name"); | ||||||||||
| // }); | ||||||||||
| // }); | ||||||||||
| }); | ||||||||||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's necessary to separate out the resources into a separate module, because we need to export them to be able to interrogate them in the tests. If we exported them from
index.tsthen they'd show up as stack outputs, which isn't what we want.This does make the template more complex, but I can't see a better way to handle it. A potential upside is that it does demonstrate how you can organize your resources into separate modules.
I'm using the generic name
infrahere for this module. We could pick a name related to the type of resource we're creating. In this case, we're creating a Bucket, so we could name this something likestorageinstead ofinfra. However, there's some concern new users might thinkstoragehas to do with some kind of Pulumi-specific storage so I'd prefer to keep it a generic name. Also, not all templates are going to be creating storage-related resources, and I'd prefer a generic name that could apply consistently to all of our templates (including the regulartypescripttemplate, which doesn't create any cloud resources). Happy to consider alternative generic names besideinfra, though.