让 AWS 生成的 AI『CodeWhisperer』来提高 CDK 测试的效率吧!

首先

本文介绍了如何利用Amazon CodeWhisperer这种生成AI来提高AWS CDK的断言测试效率。

使用AWS CDK进行断言测试时,可以确认生成的CFn模板是否与预期值相符。断言测试有以下优点和缺点。

    • メリット

IaC の変更容易性が高まる
テストコードが信頼性の高いドキュメントになる

デメリット

CloudFormation に慣れていないと難しい

ネストの深いアサーションなど珍しくなく、開発速度に影響する

通过使用CodeWhisperer,你可以从记录测试内容的文本中生成测试代码。因此,降低了断言测试的困难度,可以提升开发速度。

Amazon CodeWhisperer 是什么意思?

CodeWhisperer 是一种通过人工智能提供编码辅助服务的服务。

CodeWhisperer经过数十亿行代码的训练,可以根据评论和现有代码,在实时生成各种代码建议,从代码片段到完整函数。它可以避免耗时的编码任务,加快在不熟悉的API上构建的速度。

引用:亚马逊CodeWhisperer来源

在于2023年4月13日刚刚推出的服务中,我们逐渐感受到活用案例的增加。关于CodeWhisperer的详细信息,请参考文献中详细介绍,此处我们不再赘述。

使用Cloud9中的CodeWhisperer

在本文中,我们将使用与CodeWhisperer集成的Cloud9来编写测试代码。因此,接下来我将简要介绍在Cloud9中的设置方法。

环境

    • CDK : v2.85.0

 

    • jest : v29.6.0

 

    OS : Amazon Linux 2

准备Cloud9・CDK项目。

    1. 以管理员身份启动与AdministratorAccess权限相对应的Cloud9。

 

    准备CDK环境。例如,可以通过mkdir cdk-sample && cd cdk-sample && cdk init –language typescript来构建环境。2

CodeWhisperer 的配置

起動Cloud9后,您可以立即从CodeWhisperer那里收到提案。如果您想在任意时间接收CodeWhisperer的提案,可以执行Alt + C。

如果您无法看到建议,请检查AWS Toolkit的设置。如果设置如下所示,则可以使用CodeWhisperer。3

codewhisperer-enable.png

如果显示如下内容,请点击“简历自动建议”,CodeWhisperer 将被启用。

codewhisperer-disable.png

可以通过”Preferences”来更改与CodeWhisperer相关的详细设置。

cloud9-preferences.png

使用CodeWhisperer尝试测试驱动开发。

我們在這裡利用CodeWhisperer進行編碼,先編寫測試代碼,再編寫產品代碼。以下是處理Lambda函數的三個測試案例。

只需一个选项,用中文对以下内容进行表述:
① 检查Lambda函数的数量

测试代码

请以Lambda函数数目为1的测试用例为例,从CodeWhisperer处获取建议。只需将光标放在测试用例名称上,CodeWhisperer将会给出建议。关于建议显示的具体时机,可以参考以下示例。

lambda-count.png

按下”Enter”键接受CodeWhisperer的建议。生成的实际代码如下。

import * as cdk from 'aws-cdk-lib';
import { Template } from 'aws-cdk-lib/assertions';
import * as CdkSample from '../lib/cdk-sample-stack';

test('Lambda関数の数が1である', () => {
  const app = new cdk.App();
  // WHEN
  const stack = new CdkSample.CdkSampleStack(app, 'MyTestStack');
  // THEN
  const template = Template.fromStack(stack);
  template.resourceCountIs('AWS::Lambda::Function', 1);
}); // 末尾の「);」のみCodeWhispererの手動実行で追加

很可惜,在末尾没有写上”);”,所以我通过Alt + C手动运行了CodeWhisperer,成功地加入了。不过,这几乎是完美的测试代码。太棒了。

在这个时候运行npm test,由于没有产品代码,当然会失败。

产品代码

然后我将尝试编写产品代码。同样,我将根据 CodeWhisperer 的建议来编写,基于以下的状态。

lambda-function.png

实施了该提案后,结果如下所示。

import * as cdk from "aws-cdk-lib";
import { Construct } from "constructs";
import * as lambda from "aws-cdk-lib/aws-lambda";
import * as nodejs from "aws-cdk-lib/aws-lambda-nodejs";

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

    const nodeJsFunction = new nodejs.NodejsFunction(this, "NodeJsFunction", {
      entry: "src/lambda/index.ts",
      handler: "handler",
    });
  }
}

在这个状态下运行测试时,会出现“找不到位于src/lambda/index.ts的入口文件”的错误。由于Lambda函数的源代码文件不存在,因此导致了此错误。如果能够通过文本提示来执行文件生成就更好了,但在撰写时似乎没有这个功能,所以请使用以下命令添加文件。

$ mkdir -p src/lambda && touch src/lambda/index.ts

运行 npm test 后,测试已通过且结果为 pass。5

 PASS  test/cdk-sample.test.ts
  ✓ Lambda関数の数が1である (512 ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        1.482 s, estimated 2 s
Ran all test suites.

确认Lambda函数的运行时环境

测试代码

因为刚才是确认资源数量,所以现在尝试检查Lambda函数的属性。
为了确认Lambda函数的运行环境是Node.js 16系列,首先输入测试案例名称。请参阅以下内容。

test.png

正如上面所述,在测试用例输入过程中,它还会为我们提供建议。而且它能够选择使用的不是已过期的 Node.js14 系列,而是智能地选择使用16系列进行测试。这一点让人感觉真是像是由 AI 生成的。

根据CodeWhisperer的建议继续生成测试代码,结果如下。

test('Lambda関数のランタイムがNode.js16.xである', () => {
  const app = new cdk.App();
  // WHEN
  const stack = new CdkSample.CdkSampleStack(app, 'MyTestStack');
  // THEN
  const template = Template.fromStack(stack);
  template.hasResourceProperties('AWS::Lambda::Function', {
    Runtime: 'nodejs16.x',
  });
});

这是完美的测试代码。在断言测试中需要了解 CloudFormation,但通过使用 CodeWhisperer,负担似乎会大大减轻!

产品代码 ɡ mǎ)

然后将其添加到产品代码中。只需对原始源代码进行换行,它便给出了运行时的建议。

lambda-runtime.png

接受 CodeWhisperer 的建议的结果如下。

import * as cdk from "aws-cdk-lib";
import { Construct } from "constructs";
import * as lambda from "aws-cdk-lib/aws-lambda";
import * as nodejs from "aws-cdk-lib/aws-lambda-nodejs";

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

    const nodeJsFunction = new nodejs.NodejsFunction(this, "NodeJsFunction", {
      entry: "src/lambda/index.ts",
      handler: "handler",
+     runtime: lambda.Runtime.NODEJS_16_X,
    });
  }
}

尽管没有明确指定 Node.js 16 系列,但测试代码内容仍然相符。执行 npm test 后,测试顺利通过了。太棒了!

 PASS  test/cdk-sample.test.ts
  ✓ Lambda関数の数が1である (530 ms)
  ✓ Lambda関数のランタイムがNode.js16.xである (439 ms)

Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        1.842 s, estimated 2 s
Ran all test suites.
我对CodeWhisperer在提出建议时是否考虑了测试代码很感兴趣,但看来这只是巧合。我试着将测试代码更改为Node.js 18版本,但CodeWhisperer的建议仍然是保持在Node.js 16版本。这一点似乎也是他们将来可能会解决的问题…!

确保 Lambda 函数在私有子网中启动。

测试代码

我会进行一个稍微复杂的测试案例,来确认 Lambda 函数能在两个私有子网中启动。当我输入测试案例名称时,CodeWhisperer 给出了丰富的建议。

lambda-vpc.png

接受了提案的結果如下。

test("Lambda関数が2つのPrivate Subnetで起動される", () => {
  const app = new cdk.App();
  // WHEN
  const stack = new CdkSample.CdkSampleStack(app, "MyTestStack");
  // THEN
  const template = Template.fromStack(stack);
  template.hasResourceProperties("AWS::Lambda::Function", {
    VpcConfig: {
      SubnetIds: [
        {
          Ref: "PrivateSubnet1",
        },
        {
          Ref: "PrivateSubnet2",
        },
      ],
    },
  });
});

虽然部分Ref的值是虚拟值,但这是几乎完美的测试代码。随着嵌套的加深,测试代码的认知负担增加,所以这个建议真的太令人高兴了!

产品代码

然后将其添加到产品代码中。在产品代码中,需要添加VPC,以及将Lambda函数与VPC关联起来。
在实施时,我们采纳了CodeWhisperer的建议,并做出了以下更改。

import * as cdk from "aws-cdk-lib";
import { Construct } from "constructs";
import * as lambda from "aws-cdk-lib/aws-lambda";
import * as nodejs from "aws-cdk-lib/aws-lambda-nodejs";
+import * as ec2 from "aws-cdk-lib/aws-ec2";

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

+   const vpc = new ec2.Vpc(this, "VPC");

    const nodeJsFunction = new nodejs.NodejsFunction(this, "NodeJsFunction", {
      entry: "src/lambda/index.ts",
      handler: "handler",
      runtime: lambda.Runtime.NODEJS_16_X,
+     vpc: vpc,
    });
  }
}

在这种状态下执行测试会导致以下错误。

 FAIL  test/cdk-sample.test.ts
  ✓ Lambda関数の数が1である (570 ms)
  ✓ Lambda関数のランタイムがNode.js16.xである (506 ms)
  ✕ Lambda関数が2つのPrivate Subnetで起動される (504 ms)

  ● Lambda関数が2つのPrivate Subnetで起動される

    Template has 1 resources with type AWS::Lambda::Function, but none match as expected.
    The 1 closest matches:
    NodeJsFunction6DD2A8DD :: {
      "DependsOn": [ ... ],
      "Properties": {
        "Code": { ... },
        "Environment": { "Variables": { "AWS_NODEJS_CONNECTION_REUSE_ENABLED": "1" } },
        "Handler": "index.handler",
        "Role": { "Fn::GetAtt": [ "NodeJsFunctionServiceRole9AE046B6", "Arn" ] },
        "Runtime": "nodejs16.x",
        "VpcConfig": {
          "SecurityGroupIds": [ { "Fn::GetAtt": [ "NodeJsFunctionSecurityGroup0B3E41AF", "GroupId" ] } ],
          "SubnetIds": [
            {
    !!         Expected PrivateSubnet1 but received VPCPrivateSubnet1Subnet8BCA10E0
              "Ref": "VPCPrivateSubnet1Subnet8BCA10E0"
            },
            {
    !!         Expected PrivateSubnet2 but received VPCPrivateSubnet2SubnetCFCDAA7A
              "Ref": "VPCPrivateSubnet2SubnetCFCDAA7A"
            }
          ]
        }
      },
      "Type": "AWS::Lambda::Function"
    }

      42 |   });
      43 | });
    > 44 |
         | ^

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

Test Suites: 1 failed, 1 total
Tests:       1 failed, 2 passed, 3 total
Snapshots:   0 total
Time:        2.536 s, estimated 3 s
Ran all test suites.
由于在“!! 的预期XX但收到XX”部分友好地显示了差异,因此我将重写测试代码。

测试代码 (Cè shì mǎ)

根据测试执行结果,将测试代码进行修改。可以使用Match.stringLikeRegexp来进行部分匹配,但在这里我们简单地将测试执行结果中的received值复制并粘贴。

test("Lambda関数が2つのPrivate Subnetで起動される", () => {
  const app = new cdk.App();
  // WHEN
  const stack = new CdkSample.CdkSampleStack(app, "MyTestStack");
  // THEN
  const template = Template.fromStack(stack);
  template.hasResourceProperties("AWS::Lambda::Function", {
    VpcConfig: {
      SubnetIds: [
        {
-         Ref: "PrivateSubnet1",
+         Ref: "VPCPrivateSubnet1Subnet8BCA10E0",
        },
        {
-         Ref: "PrivateSubnet2",
+         Ref: "VPCPrivateSubnet2SubnetCFCDAA7A",
        },
      ],
    },
  });
});

再次运行npm test后,测试成功通过。

 PASS  test/cdk-sample.test.ts
  ✓ Lambda関数の数が1である (579 ms)
  ✓ Lambda関数のランタイムがNode.js16.xである (527 ms)
  ✓ Lambda関数が2つのPrivate Subnetで起動される (503 ms)

Test Suites: 1 passed, 1 total
Tests:       3 passed, 3 total
Snapshots:   0 total
Time:        2.528 s, estimated 3 s
Ran all test suites.

最后

你觉得利用CodeWhisperer的CDK进行断言测试如何呢?你觉得断言测试的门槛降低了很多吗?!
虽然在官方博客的示例中测试用例的名称是英语,但我没想到它也能很好地生成日语的测试代码,感到非常惊讶。

这次我们没有介绍,但CodeWhisperer也有安全扫描功能。结合cdk-nag进行开发,会更加安全!

希望本篇文章能對某人有所幫助。

閑話不多

我们在2023年5月20日的AWS CDK Conference Japan 2023上宣布了以下内容。

在演讲的最后,我提到了未来的计划可以利用生成AI。本演讲不仅介绍了CDK测试的优点,还介绍了许多困难之处,所以我想大家可能对断言测试感到有一定的难度。

如前所述,推荐使用CodeWhisperer进行断言测试,因为它能大大降低难度。

文献引用

    • Amazon CodeWhisperer でアプリケーションをより速く構築する10の方法

 

    • Amazon CodeWhispererでどの程度コーディングが効率化できそうか試してみた

 

    Amazon CodeWhisperer試してみた & 所感
「アサーションテストの実装例は、過去記事でもご案内しています」となります。CDK の初期設定については、「CDK Intro Workshop もご参照ください」となります。

設定の詳細については、「公式ガイドをご覧ください」となります。

実現方法についてご存知の方がいらっしゃいましたら、コメントで教えていただけると幸いです。

Lambda 関数のソースコードファイルは空のため、アプリケーションサイドのテストや実装は別途必要です。

广告
将在 10 秒后关闭
bannerAds