想象一个玩具CDK堆栈和一个S3 bucket:
import * as cdk from '@aws-cdk/core';
import * as s3 from '@aws-cdk/aws-s3';
export class BucketStack extends cdk.Stack {
constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
new s3.Bucket(this, 'MySpecificBucket');
}
}
重要的是,我们只指定了资源的id
,而没有指定它的名称(即BucketName
)。最好不要依赖特定的名称,因为它们必须是唯一的,所以即使是CDK文档也建议让CDK自动生成名称。这非常好——但是您现在如何在Jest测试用例中测试这个特定bucket的存在?
例如,如果我们有:
import * as cdk from 'aws-cdk-lib';
import { Template } from 'aws-cdk-lib/assertions';
import { BucketStack } from '../lib/bucket-stack';
let template: Template;
beforeAll(async () => {
const app = new cdk.App();
// WHEN
const stack = new BucketStack(app, 'MyTestStack', {});
// THEN
template = Template.fromStack(stack);
});
describe("My test suite", () => {
test("S3 bucket exists", () => {
template.hasResourceProperties("AWS::S3::Bucket", {
BucketName: "wont-work" // this is autogenerated!
});
});
});
模板将有如下内容:
{
MySpecificBucketF68F3FF0: {
Type: 'AWS::S3::Bucket',
UpdateReplacePolicy: 'Retain',
DeletionPolicy: 'Retain'
}
}
一个可能的解决方案是使用正则表达式检查"MySpecificBucket*"存在,但我想不能保证CDK将自动生成什么样的名称。此外,仅仅通过resourceCountIs
计算S3桶的数量是不令人满意的,因为我想断言一个特定桶的存在,而这个桶的名称我并不关心。如果我只指定了id
,我如何编写具有这些需求的测试(或者我应该以某种方式改变我的想法)?
这里有几个选项来断言具有特定ID的资源是否存在。
使用转义舱口语法断言:
const bucket = stack.node.tryFindChild("MySpecificBucket");
expect(bucket).toBeDefined();
expect(bucket instanceof s3.Bucket).toBe(true);
expect(bucket?.node.defaultChild instanceof s3.CfnBucket).toBe(true);
使用CDK测试构造和正则表达式断言:
expect(
Object.keys(template.findResources("AWS::S3::Bucket")).find((key) =>
key.match(/^MySpecificBucket[A-F0-9]{8}$/)
)
).toBeDefined();
如果您有许多这样的断言要做,请考虑快照测试。这就是CDK本身的作用。参见@aws-cdk/integ-tests-alpha模块。
他们还故意编写了一个失败的测试,从输出中获取正确的标识符,并在CDK TypeScript研讨会中修复测试。
具体来说,它们是:
$ npm run test
> cdk-workshop@0.1.0 test /home/aws-cdk-intro-workshop
> jest
FAIL test/hitcounter.test.ts
✓ DynamoDB Table Created (184ms)
✕ Lambda Has Environment Variables (53ms)
● Lambda Has Environment Variables
expect(received).toEqual(expected) // deep equality
- Expected - 2
+ Received + 2
Object {
"Variables": Object {
"DOWNSTREAM_FUNCTION_NAME": Object {
- "Ref": "TestFunctionXXXXX",
+ "Ref": "TestFunction22AD90FC",
},
"HITS_TABLE_NAME": Object {
- "Ref": "MyTestConstructHitsXXXXX",
+ "Ref": "MyTestConstructHits24A357F0",
},
},
}
37 | Environment: envCapture,
38 | });
> 39 | expect(envCapture.asObject()).toEqual(
| ^
40 | {
41 | Variables: {
42 | DOWNSTREAM_FUNCTION_NAME: {
at Object.<anonymous> (test/hitcounter.test.ts:39:33)
Test Suites: 1 failed, 1 total
Tests: 1 failed, 1 passed, 2 total
Snapshots: 0 total
Time: 3.971 s, estimated 4 s
Ran all test suites.
我想如果有更好的选择,他们是不会提倡这种方法的。