aws / aws-cdk

The AWS Cloud Development Kit is a framework for defining cloud infrastructure in code
https://aws.amazon.com/cdk
Apache License 2.0
11.57k stars 3.88k forks source link

assertions: Snapshot testing for Nested Stacks currently not supported #24445

Open Yatty121 opened 1 year ago

Yatty121 commented 1 year ago

Describe the feature

While doing snapshot testing for stacks, NestedStack does not get synth'd in the snapshot. Instead, we just see a pointer. We would like to render the root stack and all nested stacks for the snapshot tests.

Use Case

Nested stack template does not get synthesized locally so the snapshot test does not work on it. Instead of locally synthesizing it, it's uploaded to CDK staging bucket:

From https://docs.aws.amazon.com/cdk/v2/guide/stacks.html#stack_nesting At synthesis time, the nested stack is synthesized to its own AWS CloudFormation template, which is uploaded to the AWS CDK staging bucket at deployment. Nested stacks are bound to their parent stack and are not treated as independent deployment artifacts. They aren't listed by cdk list, and they can't be deployed by cdk deploy.

If I use Template.fromStack(myNestedStack), then I get something like Nested stacks must be defined within scope of another non-nested stack

FAIL test/nested_stack.test.ts
✕ Topic is created (4 ms)

● Topic is created

Nested stacks must be defined within scope of another non-nested stack

   7 |
   8 |     constructor(scope: Construct, id: string, props?: NestedStackProps) {
>  9 |       super(scope, id, props);
     |       ^
  10 |   
  11 |       const myCdkNestedTopic = new sns.Topic(this, 'myCdkNestedTopic');
  12 |   

  at findParentStack (node_modules/aws-cdk-lib/core/lib/nested-stack.js:1:4089)
  at new NestedStack (node_modules/aws-cdk-lib/core/lib/nested-stack.js:1:1091)
  at new MyNestedStack (lib/my-nested-stack.ts:9:7)
  at Object.<anonymous> (test/nested_stack.test.ts:13:18)

Test Suites: 1 failed, 1 total
Tests: 1 failed, 1 total
Snapshots: 0 total
Time: 3.455 s, estimated 4 s
Ran all test suites.

Proposed Solution

describe("DeadLetterQueue", () => {
  test("matches the snapshot", () => {
    const stack = new cdk.Stack();
    new DeadLetterQueue(stack, "DeadLetterQueue");

    const template = Template.fromStack(stack);
    expect(template.toJSON()).toMatchSnapshot();
  });
});

In an example such as above, Template.fromStack should be able to take a NestedStack construct as an input.

Other Information

No response

Acknowledgements

CDK version used

2.65.0

Environment details (OS name and version, etc.)

Any

gagipro commented 1 year ago

I was about to implement Snapshots testing and Saw this issue, we mainly use nested stacks.

usamaSaddiq commented 1 year ago

Any updates on this since then?

membr-webmaster commented 1 year ago

Came across this issue today trying to use any assertions about the template. Since the resources are in a separate nested stack, it appears that we cannot test anything against them. Here's a very simple way to replicate:

import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import { NestedStack } from 'aws-cdk-lib';
import * as sqs from 'aws-cdk-lib/aws-sqs';

class TheNestedStack extends NestedStack {
    constructor(scope: Construct, id: string, props?: cdk.NestedStackProps) {
        super(scope, id, props);

        const queue = new sqs.Queue(this, 'TheNestedQueue', {
          visibilityTimeout: cdk.Duration.seconds(300)
        });
    }
}

export class TheStack extends cdk.Stack {
    constructor(scope: Construct, id: string, props?: cdk.StackProps) {
        super(scope, id, props);

        new TheNestedStack(this, 'TheNestedStack');
    }
}

-------------------------------------------------------------------------------

import * as cdk from 'aws-cdk-lib';
import { Template } from 'aws-cdk-lib/assertions';
import * as Stacks from '../lib/cdk-test-nested-stack';

test('SQS Queue Created', () => {
  const app = new cdk.App();
  const stack = new Stacks.TheStack(app, 'MyTestStack');
  const template = Template.fromStack(stack);

  template.hasResourceProperties('AWS::SQS::Queue', {
    VisibilityTimeout: 300
  });
});

Output:

$ jest
 FAIL  test/cdk-test-nested.test.ts
  ✕ SQS Queue Created (33 ms)

  ● SQS Queue Created

    Template has 0 resources with type AWS::SQS::Queue.
    No matches found

       8 |   const template = Template.fromStack(stack);
       9 |
    > 10 |   template.hasResourceProperties('AWS::SQS::Queue', {
         |            ^
      11 |     VisibilityTimeout: 300
      12 |   });
      13 | });

      at Template.hasResourceProperties (node_modules/aws-cdk-lib/assertions/lib/template.js:1:2556)
      at Object.<anonymous> (test/cdk-test-nested.test.ts:10:12)

Test Suites: 1 failed, 1 total
Tests:       1 failed, 1 total
Snapshots:   0 total
Time:        2.954 s
Ran all test suites.
error Command failed with exit code 1.

Out of interest I also tried:

kimegede commented 10 months ago

Any updates? 🙏

rajitbanerjee commented 8 months ago

Here's a code snippet I use to generate snapshots for nested stacks.

import { Template } from "aws-cdk-lib/assertions";
import { NestedStack } from "aws-cdk-lib";

it("snapshot tests for nested stacks", () => {
        expect(Template.fromStack(myStack)).toMatchSnapshot();
        myStack.node.children
            .filter((child) => child instanceof NestedStack)
            .forEach((child) => expect(Template.fromStack(child as NestedStack)).toMatchSnapshot());
});
kimegede commented 8 months ago

Thaks @rajitbanerjee, works like a charm! Will implement that :)

nightmarefails commented 5 months ago

I see this is still open and wanted to share a snippet of code I have used to test a nested Stack

import { Template } from "aws-cdk-lib/assertions";
import { NestedStack, Stack } from "aws-cdk-lib";

it("snapshot tests for nested stacks", () => {
        const app = new App();
        const stack = new Stack(app, 'test-parent-stack', {})
        // nested Stack initialized in scope of a parent stack
        const myStack = new NestedStack(stack, 'test-nest-stack', {})
        expect(Template.fromStack(myStack)).toMatchSnapshot();
});