This Banner is For Sale !!
Get your ad here for a week in 20$ only and get upto 15k traffic Daily!!!

🏂 AWS CDK 101 🌺 – Jest testing with a TDD approach for our construct


🔰 Freshmen new to AWS CDK, please do have a look at my earlier articles one after the other on this collection.

If in case missed the earlier article, do discover it with the under hyperlinks.

🔁 Authentic earlier submit at 🔗 Dev Post

🔁 Reposted earlier submit at 🔗 dev to @aravindvcyber

On this article, allow us to introduce writing jest check circumstances that will assist us in testing our assemble which now we have created in our earlier CDK article

For simplicity, we might be solely creating check circumstances for the assemble which now we have launched. And so this doesn’t restrict you, therefore you’re free to increase this all through your venture.



Jest setup 🔅

We begin by creating a brand new folder known as check on the root of our present venture.

Add a brand new file like event-counter.check.ts

Ensure you additionally need to create a jest config file as proven under jest.config.js within the root of your venture.

module.exports = {
  testEnvironment: 'node',
  roots: ['<rootDir>/test'],
  testMatch: ['**/*.test.ts'],
  rework: {
    '^.+.tsx?$': 'ts-jest'
  }
};

Enter fullscreen mode

Exit fullscreen mode

Additionally, I exploit the under script in bundle JSON in order that I may construct and run the check case utilizing npm run check.

"check": "npm run construct && jest --coverage",
Enter fullscreen mode

Exit fullscreen mode



Testing benefits in CDK venture 🎈

  • The one benefit of testing is that we may develop our stack and make use of check suites to validate our stack even earlier than deploying to dev environments.

  • Additionally sure trivial issues which we’re very certain, might be overridden and so at all times writing the check circumstances forward of time, make it possible for the only of issues are at all times validated earlier than deployment.

We may cowl a lot of the characteristic testing within the check circumstances themselves and we can be certain that it is extremely near our expectations earlier than we deploy.

Since I’ve already composed my check circumstances, I might be utilizing jest.solely to verify we attempt one after the other.



Some helper features to take away code duplication 🎻

Additionally, I’ve outlined some reusable helper features to create the handler perform and initialize the assemble now we have created as follows.



To initialize the event-counter lambda perform ♦️

const initHandler = (stack: cdk.Stack): lambda.Perform => {
  return new lambda.Perform(stack, 'TestFunction', {
      runtime: lambda.Runtime.NODEJS_14_X,
      handler: 'event-counter.counter',
      code: lambda.Code.fromAsset('lambda')
    });
}
Enter fullscreen mode

Exit fullscreen mode



To initialize our assemble for testing 💎

const initEventCounter = (stack: cdk.Stack, handler: lambda.Perform): EventCounter => {
  return new EventCounter(stack, 'MyTestConstruct', {
    backend: handler ,
     tableName: 'Occasion Counters',
      partitionKeyName: 'Counter Identify'
  });
}
Enter fullscreen mode

Exit fullscreen mode

Additionally discover the imported modules which embrace our assemble to check, an assertions library, and the usual CDK libraries as required.

import { Template, Seize,  } from 'aws-cdk-lib/assertions';
import * as cdk from 'aws-cdk-lib';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import { EventCounter }  from '../constructs/event-counter'
Enter fullscreen mode

Exit fullscreen mode



1. Lambda has Surroundings variables 💍

Allow us to begin with the primary check case as proven under, a easy technique to verify whether or not the sources might be provisioned will not be.

check.solely('1. Lambda Has Surroundings Variables', () => {
  const stack = new cdk.Stack();
  //WHEN
  let handler = initHandler(stack);

  let eventCounter = initEventCounter(stack, handler);

  //THEN
  const template = Template.fromStack(stack);

  console.log(template);
  console.log(JSON.stringify(template));


});
Enter fullscreen mode

Exit fullscreen mode

On this check case, now we have initialized a stack and initialized the occasion counter with a brand new handler perform.



Testing technique for CDK 📝

Earlier than discussing the assertions, I’ve logged the console output to indicate you what might be our testing technique right here.

console.log
    Template {
      template: {
        Sources: {
          TestFunctionServiceRole6ABD93C7: [Object],
          TestFunction22AD90FC: [Object],
          MyTestConstructEventCountersF7DBB021: [Object],
          MyTestConstructEventCounterHandlerServiceRole7ABC2462: [Object],
          MyTestConstructEventCounterHandlerServiceRoleDefaultPolicy46018C23: [Object],
          MyTestConstructEventCounterHandler383414CF: [Object],
          MyTestConstructEventCounterHandlerLogRetention2D503F76: [Object],
          LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aServiceRole9741ECFB: [Object],
          LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aServiceRoleDefaultPolicyADDA7DEB: [Object],
          LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aFD4BFC8A: [Object]
        },
        Parameters: { BootstrapVersion: [Object] },
        Guidelines: { CheckBootstrapVersion: [Object] }
      }
    }

      at Object.<nameless> (check/event-counter.check.ts:33:11)
Enter fullscreen mode

Exit fullscreen mode

You’ll be able to see that that is the precise CDK synthesized template and we may make our check circumstances focusing on this output referenced objects.

These of you who did not perceive what I imply right here can see the small print template log, by doing a console.log(JSON.stringify(template));

JSON stringify template

This has the snapshot of the sources that we’re about to create, we may now seize components of this and carry out our assertions to resolve the check circumstances.

 template.hasResourceProperties("AWS::Lambda::Perform", {
    Surroundings: envCapture,
  });

  count on(envCapture.asObject()).toEqual(
    {
      Variables: {
        BACKEND_FUNCTION_NAME: {
          Ref: "TestFunction22AD90FC",
        },
        EVENT_COUNTER_TABLE_NAME: {
          Ref: "MyTestConstructEventCountersF7DBB021",
        },
      },
    }
  );
Enter fullscreen mode

Exit fullscreen mode

Within the above block of shut, we’re discovering a property of kind AWS::Lambda::Perform and capturing the worth of the article Surroundings.

Then we topic this to our asserts, right here the primary time we validate, we count on it to fail and as soon as we’re certain of the sources provisioned, we will replace the .toEqual to the fitting worth for the Ref: "********", which is generated based mostly in your check case and the present surroundings bootstrap template.

Lambda has environment variables



2. DynamoDB desk created ⚽

Allow us to write one other check case the place we’d confirm that just one dynamodb desk has is current as follows.

resourceCountIs technique is used to get the depend of comparable sources provisioned.

check.solely('2. DynamoDB Desk Created', () => {
  const stack = new cdk.Stack();
  // WHEN

  let handler = initHandler(stack);

  let eventCounter = initEventCounter(stack, handler);

  // THEN

  const template = Template.fromStack(stack);
  template.resourceCountIs("AWS::DynamoDB::Desk", 1);
});
Enter fullscreen mode

Exit fullscreen mode

 PASS  check/event-counter.check.ts (15.434 s)
  ✓ 1. Lambda Has Surroundings Variables (427 ms)
  ✓ 2. DynamoDB Desk Created (92 ms)
Enter fullscreen mode

Exit fullscreen mode



3. DynamoDB desk created with Encryption 🏄

Now allow us to do some TDD based mostly improvement, by creating our check case first and fixing it.

check('3. DynamoDB Desk Created With Encryption', () => {
  const stack = new cdk.Stack();
  // WHEN
  let handler = initHandler(stack);

  let eventCounter = initEventCounter(stack, handler);
  // THEN
  const template = Template.fromStack(stack);
  template.hasResourceProperties('AWS::DynamoDB::Desk', {
    SSESpecification: {
      SSEEnabled: true
    }
  });
});
Enter fullscreen mode

Exit fullscreen mode

Right here we had been anticipating a Dynamodb desk to be provisioned with some specs talked about within the assertion. It’s nothing however we predict the encryption characteristic to be turned on for the desk.

 FAIL  check/event-counter.check.ts (15.953 s)
  ✓ 1. Lambda Has Surroundings Variables (143 ms)
  ✓ 2. DynamoDB Desk Created (89 ms)
  ✕ 3. DynamoDB Desk Created With Encryption (90 ms)


  ● 3. DynamoDB Desk Created With Encryption

    The template has 1 useful resource with the sort AWS::DynamoDB::Desk, however none match as anticipated.
    The closest result's:
      {
        "Kind": "AWS::DynamoDB::Desk",
        "Properties": {
          "KeySchema": [
            {
              "AttributeName": "Counter Name",
              "KeyType": "HASH"
            }
          ],
          "AttributeDefinitions": [
            {
              "AttributeName": "Counter Name",
              "AttributeType": "S"
            }
          ],
          "ProvisionedThroughput": {
            "ReadCapacityUnits": 5,
            "WriteCapacityUnits": 5
          }
        },
        "UpdateReplacePolicy": "Delete",
        "DeletionPolicy": "Delete"
      }
    with the next mismatches:
      Lacking key at /Properties/SSESpecification (utilizing objectLike matcher)

      74 |   // THEN
      75 |   const template = Template.fromStack(stack);
    > 76 |   template.hasResourceProperties('AWS::DynamoDB::Desk', {
         |            ^
      77 |     SSESpecification:      

      at Template.hasResourceProperties (node_modules/aws-cdk-lib/assertions/lib/template.ts:62:36)
      at Object.<nameless> (check/event-counter.check.ts:76:12)
Enter fullscreen mode

Exit fullscreen mode

As anticipated the check case failed, we may add the required characteristic immediately, by updating our constructevent-counter.ts as follows whereas we’re defining the dynamodb.


 const Counters = new dynamodb.Desk(this, tableName, {
        partitionKey: { title: partitionKeyName, kind: dynamodb.AttributeType.STRING },
       encryption: dynamodb.TableEncryption.AWS_MANAGED, //added for TDD

    });

Enter fullscreen mode

Exit fullscreen mode

Sure, now we have received that proper now.

 PASS  check/event-counter.check.ts (5.077 s)
  ✓ 1. Lambda Has Surroundings Variables (138 ms)
  ✓ 2. DynamoDB Desk Created (93 ms)
  ✓ 3. DynamoDB Desk Created With Encryption (84 ms)
Enter fullscreen mode

Exit fullscreen mode

By default, the Dynamodb constructor will provision sources with default specs like 5 learn items and 5 write items. However we might have to have it tweak a bit based mostly on environments and particular peak load expectations of the learn and write patterns we count on.

Allow us to add yet another check case as follows:



4. learn capability may be configured 🎼

The primary couple of failure causes the place we would like the capability to be restricted are as follows.

check('4. learn capability may be configured', () => {
  const stack = new cdk.Stack();

  count on(() => {
    let handler = 
    new EventCounter(stack, 'MyTestConstruct', {
    backend: handler ,
     tableName: 'Occasion Counters',
      partitionKeyName: 'Counter Identify',
      readCapacity: 30,
      writeCapacity: 15
  });

  }).toThrowError(/readCapacity have to be better than 5 and fewer than 20/);
});
Enter fullscreen mode

Exit fullscreen mode



5. write capability ought to be within the vary of 5 to 10 🎺

check('5. write capability ought to be within the vary of 5 to 10', () => {
  const stack = new cdk.Stack();

  count on(() => {
    let handler = initHandler(stack);
    new EventCounter(stack, 'MyTestConstruct', {
    backend: handler ,
     tableName: 'Occasion Counters',
      partitionKeyName: 'Counter Identify',
      readCapacity: 5,
      writeCapacity: 15
  });

  }).toThrowError(/writeCapacity have to be better than 5 and fewer than 10/);
});

Enter fullscreen mode

Exit fullscreen mode

To allow this now we have so as to add some code to the constructsevent-counter.ts to throw essential exceptions.


export interface EventCounterProps {
  /** the perform for which we need to depend Occasion messages**/
  backend: lambda.IFunction,
  tableName: string,
  partitionKeyName: string,
  readCapacity?: quantity,
  writeCapacity?: quantity

}
    constructor(scope: Assemble, id: string, props: EventCounterProps) {

    if (props.readCapacity !== undefined && (props.readCapacity < 5 || props.readCapacity > 20)) {
      throw new Error('readCapacity have to be better than 5 and fewer than 20');
    }

    if (props.writeCapacity !== undefined && (props.writeCapacity < 5 || props.writeCapacity > 10)) {
      throw new Error('writeCapacity have to be better than 5 and fewer than 10');
    }

    tremendous(scope, id);

    .......
Enter fullscreen mode

Exit fullscreen mode

const Counters = new dynamodb.Desk(this, tableName, {
        partitionKey: { title: partitionKeyName, kind: dynamodb.AttributeType.STRING },
        encryption: dynamodb.TableEncryption.AWS_MANAGED,
        readCapacity: props.readCapacity ?? 5,
        writeCapacity: props.writeCapacity ?? 5
    });
Enter fullscreen mode

Exit fullscreen mode

You possibly can establish now that now we have added logic to throw an exception when our anticipated vary will not be met for the learn and proper capability within the assertions statements with toThrowError.

 PASS  check/event-counter.check.ts (14.978 s)
  ✓ 1. Lambda Has Surroundings Variables (145 ms)
  ✓ 2. DynamoDB Desk Created (87 ms)
  ✓ 3. DynamoDB Desk Created With Encryption (94 ms)
  ✓ 4. learn capability may be configured (34 ms)
  ✓ 5. write capability ought to be within the vary of 5 to 10 (4 ms)
Enter fullscreen mode

Exit fullscreen mode



6. DynamoDB Desk Created With pattern learn and write items as effectively 🎯

Equally, we will add a check case with out exception to simulate the optimistic check case as effectively for learn and write capability items.


check.solely('6. DynamoDB Desk Created With pattern learn and write items as effectively', () => {
  const stack = new cdk.Stack();
  // WHEN
   let handler = initHandler(stack);
  new EventCounter(stack, 'MyTestConstruct', {
    backend: handler ,
     tableName: 'Occasion Counters',
      partitionKeyName: 'Counter Identify',
      readCapacity: 10,
      writeCapacity: 5
  });
  // THEN
  const template = Template.fromStack(stack);
  template.hasResourceProperties('AWS::DynamoDB::Desk', 
 {
          "KeySchema": [
            {
              "AttributeName": "Counter Name",
              "KeyType": "HASH"
            }
          ],
          "AttributeDefinitions": [
            {
              "AttributeName": "Counter Name",
              "AttributeType": "S"
            }
          ],
          "ProvisionedThroughput": {
            "ReadCapacityUnits": 10,
            "WriteCapacityUnits": 5
          },
          "SSESpecification": {
            "SSEEnabled": true
          }
        }
  );
});
Enter fullscreen mode

Exit fullscreen mode

 PASS  check/event-counter.check.ts (14.978 s)
  ✓ 1. Lambda Has Surroundings Variables (145 ms)
  ✓ 2. DynamoDB Desk Created (87 ms)
  ✓ 3. DynamoDB Desk Created With Encryption (94 ms)
  ✓ 4. learn capability may be configured (34 ms)
  ✓ 5. write capability ought to be within the vary of 5 to 10 (4 ms)
  ✓ 6. DynamoDB Desk Created With pattern learn and write items as effectively(60 ms)
Enter fullscreen mode

Exit fullscreen mode

The sixth check case additionally helped our check case to achieve one hundred pc check protection as follows.


------------------|---------|----------|---------|---------|-------------------
File              | % Stmts | % Department | % Funcs | % Strains | Uncovered Line #s
------------------|---------|----------|---------|---------|-------------------
All recordsdata         |   90.47 |       50 |     100 |   90.47 |
 event-counter.ts |   90.47 |       50 |     100 |   90.47 | 28,32
------------------|---------|----------|---------|---------|-------------------

------------------|---------|----------|---------|---------|-------------------
File              | % Stmts | % Department | % Funcs | % Strains | Uncovered Line #s
------------------|---------|----------|---------|---------|-------------------
All recordsdata         |     100 |      100 |     100 |     100 |
 event-counter.ts |     100 |      100 |     100 |     100 |
------------------|---------|----------|---------|---------|-------------------
Enter fullscreen mode

Exit fullscreen mode

Nonetheless now we have proper extra check circumstances based mostly on the practical expectations on the vital elements of the constructs.



7. Lambda has log retention specified with 30 days 🎸


check.solely('7. Lambda Has logRetention specified with 30 days', () => {
  const stack = new cdk.Stack();
  // WHEN
   let handler = initHandler(stack);
  new EventCounter(stack, 'MyTestConstruct', {
    backend: handler ,
     tableName: 'Occasion Counters',
      partitionKeyName: 'Counter Identify',
      readCapacity: 10,
      writeCapacity: 5
  });
  // THEN
  const template = Template.fromStack(stack);
  const cp1 = new Seize();
  const cp2 = new Seize();
  template.hasResourceProperties("Customized::LogRetention", {
    LogGroupName: cp1,
    RetentionInDays: cp2

  });

  count on(cp1.asObject()).toEqual(
    {
          "Fn::Be part of": [
            "",
            [
              "/aws/lambda/",
              {
                "Ref": "MyTestConstructEventCounterHandler383414CF"
              }
            ]
          ]
        }
  );
  count on(cp2.asNumber()).toEqual(
    30
  );
});
Enter fullscreen mode

Exit fullscreen mode

The above check case exhibits how we will do numerical and object degree assertions and likewise tell us the best way to seize a phase of the code into variables to carry out assertions.



8. Lambda Has read-write entry on dynamodb and may invoke backend perform 🎀

The under check case is used to validate the IAM coverage ensuring the lambda perform can carry out write operations on dynamodb created and may invoke the backend handler perform.

Right here an assertion is carried out with a captured variable as asArray


check.solely('8. Lambda Has learn write entry on dynamodb and may invoke backend perform ', () => {
  const stack = new cdk.Stack();
  // WHEN
   let handler = new lambda.Perform(stack, 'TestFunction', {
      runtime: lambda.Runtime.NODEJS_14_X,
      handler: 'event-counter.counter',
      code: lambda.Code.fromAsset('lambda')
    });
  let counter = new EventCounter(stack, 'MyTestConstruct', {
    backend: handler ,
     tableName: 'Occasion Counters',
      partitionKeyName: 'Counter Identify',
      readCapacity: 10,
      writeCapacity: 5
  });


  // THEN
  const template = Template.fromStack(stack);
  const cp = new Seize();

  template.hasResourceProperties("AWS::IAM::Coverage", {
    PolicyDocument: {Assertion: cp}
  });

  count on(cp.asArray()).toEqual(
      [
      {
        "Action": [
          "dynamodb:BatchGetItem",
          "dynamodb:GetRecords",
          "dynamodb:GetShardIterator",
          "dynamodb:Query",
          "dynamodb:GetItem",
          "dynamodb:Scan",
          "dynamodb:ConditionCheckItem",
          "dynamodb:BatchWriteItem",
          "dynamodb:PutItem",
          "dynamodb:UpdateItem",
          "dynamodb:DeleteItem"
        ],
        "Impact": "Permit",
        "Useful resource": [
          {
            "Fn::GetAtt": [
              "MyTestConstructEventCountersF7DBB021",
              "Arn"
            ]
          },
          {
            "Ref": "AWS::NoValue"
          }
        ]
      },
      {
        "Motion": "lambda:InvokeFunction",
        "Impact": "Permit",
        "Useful resource": {
          "Fn::GetAtt": [
            "TestFunction22AD90FC",
            "Arn"
          ]
        }
      }
    ]

  );

});
Enter fullscreen mode

Exit fullscreen mode

  PASS  check/event-counter.check.ts (14.928 s)
  ✓ 1. Lambda Has Surroundings Variables (139 ms)
  ✓ 2. DynamoDB Desk Created (91 ms)
  ✓ 3. DynamoDB Desk Created With Encryption (76 ms)
  ✓ 4. learn capability may be configured (29 ms)
  ✓ 5. write capability ought to be within the vary of 5 to 10 (9 ms)
  ✓ 6. DynamoDB Desk Created With pattern learn and write items as effectively (61 ms)
  ✓ 7. Lambda Has logRetention specified with 30 days (50 ms)
  ✓ 8. Lambda Has learn write entry on dynamodb and may invoke backend perform  (31 ms)

------------------|---------|----------|---------|---------|-------------------
File              | % Stmts | % Department | % Funcs | % Strains | Uncovered Line #s
------------------|---------|----------|---------|---------|-------------------
All recordsdata         |     100 |      100 |     100 |     100 |
 event-counter.ts |     100 |      100 |     100 |     100 |
------------------|---------|----------|---------|---------|-------------------
Take a look at Suites: 1 handed, 1 whole
Exams:       8 handed, 8 whole
Snapshots:   0 whole
Time:        15.068 s
Ran all check suites.
Enter fullscreen mode

Exit fullscreen mode



Conclusion for testing in CDK 🏂

Thus now we have demonstrated how we will write in depth check circumstances for our assemble and venture modules, utilizing jest.

You can too make use of the file ./cdk.out/CommonEventStack.template.json as a reference, which could have the total template for the stack to be provisioned to jot down extra comparable built-in check circumstances on the venture degree with out deploying to the precise surroundings.

We’ll add extra connections to this API gateway and lambda stack and make it extra usable within the upcoming articles, so do take into account following and subscribing to my e-newsletter.

🎉 Thanks for supporting! 🙏

Could be nice in case you prefer to ☕ Buy Me a Coffee, to assist increase my efforts.

🔁 Authentic submit at 🔗 Dev Post

🔁 Reposted at 🔗 dev to @aravindvcyber

The Article was Inspired from tech community site.
Contact us if this is inspired from your article and we will give you credit for it for serving the community.

This Banner is For Sale !!
Get your ad here for a week in 20$ only and get upto 10k Tech related traffic daily !!!

Leave a Reply

Your email address will not be published. Required fields are marked *

Want to Contribute to us or want to have 15k+ Audience read your Article ? Or Just want to make a strong Backlink?