让 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项目。
-
- 以管理员身份启动与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 将被启用。

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

使用CodeWhisperer尝试测试驱动开发。
我們在這裡利用CodeWhisperer進行編碼,先編寫測試代碼,再編寫產品代碼。以下是處理Lambda函數的三個測試案例。
只需一个选项,用中文对以下内容进行表述:
① 检查Lambda函数的数量
测试代码
请以Lambda函数数目为1的测试用例为例,从CodeWhisperer处获取建议。只需将光标放在测试用例名称上,CodeWhisperer将会给出建议。关于建议显示的具体时机,可以参考以下示例。

按下”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 的建议来编写,基于以下的状态。

实施了该提案后,结果如下所示。
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系列,首先输入测试案例名称。请参阅以下内容。

正如上面所述,在测试用例输入过程中,它还会为我们提供建议。而且它能够选择使用的不是已过期的 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ǎ)
然后将其添加到产品代码中。只需对原始源代码进行换行,它便给出了运行时的建议。

接受 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.
确保 Lambda 函数在私有子网中启动。
测试代码
我会进行一个稍微复杂的测试案例,来确认 Lambda 函数能在两个私有子网中启动。当我输入测试案例名称时,CodeWhisperer 给出了丰富的建议。

接受了提案的結果如下。
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.
测试代码 (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試してみた & 所感
設定の詳細については、「公式ガイドをご覧ください」となります。
実現方法についてご存知の方がいらっしゃいましたら、コメントで教えていただけると幸いです。
Lambda 関数のソースコードファイルは空のため、アプリケーションサイドのテストや実装は別途必要です。