skip to Main Content

Imagine a toy CDK stack with one S3 bucket:

    import * as cdk from '@aws-cdk/core';
    import * as s3 from '@aws-cdk/aws-s3';
    
    export class BucketStack extends cdk.Stack {
     constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
       super(scope, id, props);
    
       new s3.Bucket(this, 'MySpecificBucket');
     }
    }

Importantly, we have specified only the id of the resource and not its name (i.e., BucketName). It’s good practice not to rely on a specific name since they have to be unique, so even the CDK docs recommend letting CDK autogenerate the name. This is perfectly fine – but how do you now test for the existence of this specific bucket in a Jest test case?

For instance, if we had:

    import * as cdk from 'aws-cdk-lib';
    import { Template } from 'aws-cdk-lib/assertions';
    import { BucketStack  } from '../lib/bucket-stack';
    
    let template: Template;
    
    beforeAll(async () => {
        const app = new cdk.App();
        // WHEN
        const stack = new BucketStack(app, 'MyTestStack', {});
        // THEN
        template = Template.fromStack(stack);
    });
    
    describe("My test suite", () => {
        test("S3 bucket exists", () => {
            template.hasResourceProperties("AWS::S3::Bucket", {
                BucketName: "wont-work" // this is autogenerated!
            });
        });
    });

The template will have something like:

    {
       MySpecificBucketF68F3FF0: {
         Type: 'AWS::S3::Bucket',
         UpdateReplacePolicy: 'Retain',
         DeletionPolicy: 'Retain'
       }
    }

A potential solution could check using a regex for "MySpecificBucket*" to exists, but I guess there are no guarantees what kind of a name CDK will autogenerate. Also, it’s not satisfactory to just count the number of S3 buckets via resourceCountIs because I want to assert the existence of a specific bucket whose name I specifically don’t care about. If I just have the id specified, how can I write a test with these requirements (or should I somehow change my thinking)?

2

Answers


  1. Chosen as BEST ANSWER

    FWIW, they also intentionally write a test that fails, grab the correct identifier from the output, and fix the test in the CDK TypeScript workshop.

    Specifically, they do:

    $ npm run test
    
    > [email protected] test /home/aws-cdk-intro-workshop
    > jest
    
     FAIL  test/hitcounter.test.ts
      ✓ DynamoDB Table Created (184ms)
      ✕ Lambda Has Environment Variables (53ms)
    
      ● Lambda Has Environment Variables
    
        expect(received).toEqual(expected) // deep equality
    
        - Expected  - 2
        + Received  + 2
    
          Object {
            "Variables": Object {
              "DOWNSTREAM_FUNCTION_NAME": Object {
        -       "Ref": "TestFunctionXXXXX",
        +       "Ref": "TestFunction22AD90FC",
              },
              "HITS_TABLE_NAME": Object {
        -       "Ref": "MyTestConstructHitsXXXXX",
        +       "Ref": "MyTestConstructHits24A357F0",
              },
            },
          }
    
          37 |     Environment: envCapture,
          38 |   });
        > 39 |   expect(envCapture.asObject()).toEqual(
             |                                 ^
          40 |     {
          41 |       Variables: {
          42 |         DOWNSTREAM_FUNCTION_NAME: {
    
          at Object.<anonymous> (test/hitcounter.test.ts:39:33)
    
    Test Suites: 1 failed, 1 total
    Tests:       1 failed, 1 passed, 2 total
    Snapshots:   0 total
    Time:        3.971 s, estimated 4 s
    Ran all test suites.
    

    I suppose they wouldn't advocate for this approach if there was a better alternative.


  2. Here are several options to assert the presence of a resource with a specific ID.

    Assert with escape hatch syntax:

    const bucket = stack.node.tryFindChild("MySpecificBucket");
    
    expect(bucket).toBeDefined();
    expect(bucket instanceof s3.Bucket).toBe(true);
    expect(bucket?.node.defaultChild instanceof s3.CfnBucket).toBe(true);
    

    Assert with the CDK testing construct and regex:

    expect(
      Object.keys(template.findResources("AWS::S3::Bucket")).find((key) =>
        key.match(/^MySpecificBucket[A-F0-9]{8}$/)
      )
    ).toBeDefined();
    

    If you have many such assertions to make, consider snapshot testing. This is what the CDK itself does. See the @aws-cdk/integ-tests-alpha module.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search