Thursday, February 2, 2023

 

Among all the established cloud migration patterns for different workloads, one pattern stands alone because it is not specific to a resource. This is the pattern for software development and testing in the cloud so that the logic can be incrementally migrated from an on-premises application to say, serverless computing. With the elimination of a one-time deployment of migrated code, the path to incremental delivery and enhancement of logic that is hosted in the cloud becomes more gradual, deliberate and even expanding audience. Versioning of the logic helps keep existing customers while onboarding new ones.

Let us take a specific example of a NodeJs application developed with GitHub and build with AWS CodeBuild. The instructions in this document helps to set up a continuous integration and continuous delivery workflow that runs unit-tests from a GitHub repository. Unit-tests reduce refactoring time while helping engineers to get up to speed on their code base more quickly, and provide confidence in the expected behavior. It involves testing individual functions including Lambda functions. Use of an AWS Cloud9 instance which is an Integrated Development Environment is suggested but not mandatory. It can be accessed even through a web browser.

Setting up this pipeline involves a few milestones which developers call ‘epics’. The first epic involves running unit-tests on a personal GitHub repository with CodeBuild. The tasks involved are:

Sign in to the AWS Management Console and open the CodeBuild console at https://console.aws.amazon.com/codesuite/codebuild/home

Create a build project and in the project configuration, type the name of the project

In the source section, specify the provider as GitHub and point to the existing personal Repository in the GitHub account by specifying its URL.

In the primary source webhook events section, specify to rebuild every time a code change is pushed to this repository.

In the environment section, choose managed image and the latest image for an Amazon Linux instance.

Leave the default settings and complete the project creation.

Start the build.

The CodeBuild console will display the tests run and the unit-test results can be reviewed. These results validate the repository integration with the project that has been created with the steps above. When the webhook is applied, code changes automatically start a build.

Unit-tests often involve assertion, spies, stubs and mocks.

An assertion is used to verify an expected result. For example, the following code validates that the results are in a given range:

Describe(‘Target Function Group’, () => {

 It(‘Check that the result is between 0 and 1000’, function () {

              const target = new Target();

             expect(target.id).is.above(0).but.below(1000)

});

});

A spy is used to observe what is happening when a function is running. The following example shows whether a set of methods were invoked.

Describe(‘Target Function Group’, () => {

It (‘should verify that the proper methods were called’, () => {

     const spyStart = spy(Target.prototype, “start”);

     const spyStop = spy(Target.prototype, “stop”);

     const target = new Target();

      target.start();

      target.stop();

      expect(spyStart.called).to.be.true;

      expect(spyStop.called).to.be.true;

});

});

A stub is used to override a function’s default response. For example, a stub can be used to force a return ID from the getId function

Describe(‘Target Function Group’, () => {

         It (‘Check that the Target Id is between 0 and 1000’, function () {

             let generateIdStub = stub(Target.prototype, ‘getId’).returns(99999);

             const target = new Target();

            expect(target.getId).is.equal(99999);

            generateIdStub.restore();

});

});

A mock is a fake method that has predefined behavior for testing different scenarios. A mock can be considered an extended form of a stub and can carry out multiple tasks simultaneously. For example, a mock can be used to validate three scenarios: 1. A function is called, 2. That it is called with arguments. and 3. It returns an integer, say, 9.

Describe(‘Target Function Group’, () => {

    It (‘Check that the TargetId is between 0 and 1000’, function() {

let mock = mock(Target.prototype).expects(‘getId’).withArgs().returns(9);

const target = new Target();

const id = target.getId();

mock.verify();

expect(id).is.equal(9);

});

});

No comments:

Post a Comment