综合测试
本单元中更高层次的构件的API是实验性的并正在积极开发中. 在今后任何版本中,它们都须作非背向相容的修改或去除。 这些是 不适用 语义版本 模型和断开的更改 在发行笔记中宣布. 这意味着虽然你可能使用它们,但可能需要更新 当更新到此软件包的更新版本时,您的源代码.
概览
此库意在结合 integ 运行者 国 际 使用户能够为 AWS CDK Constructions 编写和执行集成测试.
集成测试应被定义为CDK应用程序,以及 集成测试和CDK应用程序之间应当有1:1的关系.
举例来说,为了创造一个叫做 my-function
我们需要建立一个文件 包含我们的整合测试应用程序.
测试/输入我的功能
const app = new App();
const stack = new Stack();
new lambda.Function(stack, 'MyFunction', {
runtime: lambda.Runtime.NODEJS_LATEST,
handler: 'index.handler',
code: lambda.Code.fromAsset(path.join(__dirname, 'lambda-handler')),
});
这是一个自封的 CDK 应用程序, 我们可以通过运行来部署
cdk deploy --app 'node test/integ.my-function.js'
为了将它变成一个综合测试,需要的就是
使用该 IntegTest 构造.
declare const app: App;
declare const stack: Stack;
new IntegTest(app, 'Integ', { testCases: [stack] });
你会注意到 stack 已注册到 IntegTest 作为测试案例.
每个整合测试可以包含多个测试案例,这些仅仅是实例
一个堆。 见 使用量 详细情况.
使用量
综合测试
假设你有一个简单的堆栈, 它只封装一个Lambda函数 与一个 某些处理器 :
interface StackUnderTestProps extends StackProps {
architecture?: lambda.Architecture;
}
class StackUnderTest extends Stack {
constructor(scope: Construct, id: string, props: StackUnderTestProps) {
super(scope, id, props);
new lambda.Function(this, 'Handler', {
runtime: lambda.Runtime.NODEJS_LATEST,
handler: 'index.handler',
code: lambda.Code.fromAsset(path.join(__dirname, 'lambda-handler')),
architecture: props.architecture,
});
}
}
您可能想要在不同的条件下测试这个堆栈 。 例如,我们希望
这个要正确部署的堆栈, 不管我们选择哪种架构
用于Lambda函数。 特别是,它应当为双方服务 ARM_64 财务报告和审定财务报表
X86_64。这样你就可以创建 IntegTestCase 运用两种情景:
interface StackUnderTestProps extends StackProps {
architecture?: lambda.Architecture;
}
class StackUnderTest extends Stack {
constructor(scope: Construct, id: string, props: StackUnderTestProps) {
super(scope, id, props);
new lambda.Function(this, 'Handler', {
runtime: lambda.Runtime.NODEJS_LATEST,
handler: 'index.handler',
code: lambda.Code.fromAsset(path.join(__dirname, 'lambda-handler')),
architecture: props.architecture,
});
}
}
// Beginning of the test suite
const app = new App();
new IntegTest(app, 'DifferentArchitectures', {
testCases: [
new StackUnderTest(app, 'Stack1', {
architecture: lambda.Architecture.ARM_64,
}),
new StackUnderTest(app, 'Stack2', {
architecture: lambda.Architecture.X86_64,
}),
],
});
这是所有你需要的指令 整合测试跑者知道 用于合成、部署和销毁。 但你也许也需要 通过修改其参数来自定义跑者的行为 。 例如:
const app = new App();
const stackUnderTest = new Stack(app, 'StackUnderTest', /* ... */);
const stack = new Stack(app, 'stack');
const testCase = new IntegTest(app, 'CustomizedDeploymentWorkflow', {
testCases: [stackUnderTest],
diffAssets: true,
stackUpdateWorkflow: true,
cdkCommandOptions: {
deploy: {
args: {
requireApproval: RequireApproval.NEVER,
json: true,
},
},
destroy: {
args: {
force: true,
},
},
},
});
集成测试集
在大多数情况下,一体化测试将包含一个单一测试 IntegTestCase。 。 。 .
默认创建时 IntegTest a 一个 IntegTestCase 为您创建
所有测试案例都登记在案 IntegTestCase编辑 IntegTestCase
财务报告和审定财务报表 IntegTestCaseStack 只有在下列情况下才需要建筑:
为单个测试案例确定了不同的选项.
例如,你可能想要有一个测试案例,其中 diffAssets 已启用.
declare const app: App;
declare const stackUnderTest: Stack;
const testCaseWithAssets = new IntegTestCaseStack(app, 'TestCaseAssets', {
diffAssets: true,
});
new IntegTest(app, 'Integ', { testCases: [stackUnderTest, testCaseWithAssets] });
请求
该图书馆还提供一种用途,用以对综合测试部署的基础设施进行断言.
提出断言的主要情况有两种.
- 使用
integ-runner
在此情况下,您将使用 IntegTest 构造,然后使用 assert 属性.
你应该 没有 直接使用断言构造,但应当使用 methods 打开 IntegTest.assertions。 。 。 .
declare const app: App;
declare const stack: Stack;
const integ = new IntegTest(app, 'Integ', { testCases: [stack] });
integ.assertions.awsApiCall('S3', 'getObject');
默认情况下, 您将自动生成一个声明堆栈 。 不过,您可以提供自己的堆栈供使用.
declare const app: App;
declare const stack: Stack;
declare const assertionStack: Stack;
const integ = new IntegTest(app, 'Integ', { testCases: [stack], assertionStack: assertionStack });
integ.assertions.awsApiCall('S3', 'getObject');
- 常规 CDK 部署的一部分
在此情况下,您可能将断言作为普通CDK部署的一部分,以便对基础设施做出断言 在部署被视为成功之前。 在此情况下,您可以直接使用这些断言.
declare const myAppStack: Stack;
new AwsApiCall(myAppStack, 'GetObject', {
service: 'S3',
api: 'getObject',
});
部署
通过使用 DeployAssert 构造。 这种构造造就了自己的 Stack 从
作为整合测试的一部分而创建的任何堆栈。 这个 Stack 与其他堆栈不同
编辑 integ-runner 工具。 例如,此堆栈将不会被 integ-runner。 。 。 .
DeployAssert 此外,还提供各种公用事业,以登记您自己的说法.
declare const myCustomResource: CustomResource;
declare const stack: Stack;
declare const app: App;
const integ = new IntegTest(app, 'Integ', { testCases: [stack] });
integ.assertions.expect(
'CustomAssertion',
ExpectedResult.objectLike({ foo: 'bar' }),
ActualResult.fromCustomResource(myCustomResource, 'data'),
);
在上述例子中,创建了一个将触发定义的用户的断言 CustomResource
并断言 data 属性等于 { foo: 'bar' }。 。 。 .
API 调用
将“实际”结果与预期结果相比较的一种常用方法是 API呼叫接收一些数据. 此库使用 CloudFormation 自定义资源来完成此任务 这意味着CloudFormation将召唤出一个Lambda函数,它将 给API打电话.
HttpApi 电话
使用 HttpApiCall 将使用
节点比对 JavaScript 库到
给HTTP打电话.
可以通过使用类目录(在正常部署的情况下):
declare const stack: Stack;
new HttpApiCall(stack, 'MyAsssertion', {
url: 'https://example-api.com/abc',
});
或者通过使用 httpApiCall 方法打开 DeployAssert (写集成测试时):
declare const app: App;
declare const stack: Stack;
const integ = new IntegTest(app, 'Integ', {
testCases: [stack],
});
integ.assertions.httpApiCall('https://example-api.com/abc');
奥斯阿皮呼吁
使用 AwsApiCall 构造将使用 AWS JavaScript SDK 来调用 API .
可以通过使用类目录(在正常部署的情况下):
declare const stack: Stack;
new AwsApiCall(stack, 'MyAssertion', {
service: 'SQS',
api: 'receiveMessage',
parameters: {
QueueUrl: 'url',
},
});
或者通过使用 awsApiCall 方法打开 DeployAssert (写集成测试时):
declare const app: App;
declare const stack: Stack;
const integ = new IntegTest(app, 'Integ', {
testCases: [stack],
});
integ.assertions.awsApiCall('SQS', 'receiveMessage', {
QueueUrl: 'url',
});
您必须指定 service 编辑 api 使用时 AwsApiCall 构造.
这个 service 是AWS服务的名称,以下列形式之一:
- JavaScript v3 软件包名称的 AWS SDK ()
@aws-sdk/client-api-gateway(中文(简体) ) - JavaScript v3 客户端名的 AWS SDK ()
api-gateway(中文(简体) ) - JavaScript v2 构造器名称的 AWS SDK (
APIGateway(中文(简体) ) - JavaScript v2 构造器名称的小写 AWS SDK(
apigateway(中文(简体) )
这个 api 是AWS API呼叫的名称,以下列形式之一:
- API 参考文档中的 API 调用名称(
GetObject(中文(简体) ) - API 调用名称从小写字母开始(
getObject(中文(简体) ) - JavaScript v3 命令类名的 AWS SDK ()
GetObjectCommand(中文(简体) )
默认情况下 AwsApiCall 构造将自动添加正确的 IAM 策略
允许 Lambda 函数调用 API。 它这样做的依据是: service
财务报告和审定财务报表 api 这是提供的。 在上述例子中,服务是: SQS 亚庇是
receiveMessage 因此,它将创建一个政策与 Action: 'sqs:ReceiveMessage。 。 。 .
有些情况下权限与服务/api呼叫不完全一致,因为
实例 S3 listObjectsV2 api. (英语). 在这种情况下,可以增加正确的政策
通过访问 provider 对象.
declare const app: App;
declare const stack: Stack;
declare const integ: IntegTest;
const apiCall = integ.assertions.awsApiCall('S3', 'listObjectsV2', {
Bucket: 'mybucket',
});
apiCall.provider.addToRolePolicy({
Effect: 'Allow',
Action: ['s3:GetObject', 's3:ListBucket'],
Resource: ['*'],
});
执行时 waitForAssertion(),需要使用 waiterProvider.addToRolePolicy()。 。 。 .
因为 IApiCall 没有 waiterProvider 财产,你需要把它投给 AwsApiCall。 。 。 .
declare const integ: IntegTest;
const apiCall = integ.assertions.awsApiCall('S3', 'listObjectsV2', {
Bucket: 'mybucket',
}).waitForAssertions() as AwsApiCall;
apiCall.waiterProvider?.addToRolePolicy({
Effect: 'Allow',
Action: ['s3:GetObject', 's3:ListBucket'],
Resource: ['*'],
});
注意添加 ToRole Policy () 使用直接的 IAM JSON 政策 blops, 而不是 iams 。 政策说明 类似您在 CDK 其余部分看到的对象.
等同处理
这个库目前提供了一种能力,可以断言两个价值观是平等的
通过使用 EqualsAssertion 课。 这个是用蓝巴达的
备份 CustomResource 单位之三使用 匹配 从工具
@aws-cdk/ servitions (英语) 图书馆.
declare const app: App;
declare const stack: Stack;
declare const queue: sqs.Queue;
declare const fn: lambda.IFunction;
const integ = new IntegTest(app, 'Integ', {
testCases: [stack],
});
integ.assertions.invokeFunction({
functionName: fn.functionName,
invocationType: InvocationType.EVENT,
payload: JSON.stringify({ status: 'OK' }),
});
const message = integ.assertions.awsApiCall('SQS', 'receiveMessage', {
QueueUrl: queue.queueUrl,
WaitTimeSeconds: 20,
});
message.assertAtPath('Messages.0.Body', ExpectedResult.objectLike({
requestContext: {
condition: 'Success',
},
requestPayload: {
status: 'OK',
},
responseContext: {
statusCode: 200,
},
responsePayload: 'success',
}));
匹配
integ-tests 还提供a Match 类似于 @aws-cdk/assertions 模块. Match
可用于构建 ExpectedResult虽然功能相似,但目前只有一组方法可用 Match 此模块的实用性 : arrayWith, (中文(简体) ), objectLike, (中文(简体) ), stringLikeRegexp 财务报告和审定财务报表 serializedJson。 。 。 .
declare const message: AwsApiCall;
message.expect(ExpectedResult.objectLike({
Messages: Match.arrayWith([
{
Payload: Match.serializedJson({ key: 'value' }),
},
{
Body: {
Values: Match.arrayWith([{ Asdf: 3 }]),
Message: Match.stringLikeRegexp('message'),
},
},
]),
}));
实例
启动 Lambda 函数
在这个例子中,有一个Lambda函数被援引,并且 我们断言返回的有效载荷等于"200".
declare const lambdaFunction: lambda.IFunction;
declare const app: App;
const stack = new Stack(app, 'cdk-integ-lambda-bundling');
const integ = new IntegTest(app, 'IntegTest', {
testCases: [stack],
});
const invoke = integ.assertions.invokeFunction({
functionName: lambdaFunction.functionName,
});
invoke.expect(ExpectedResult.objectLike({
Payload: '200',
}));
以上示例默认会创建从未存在的 Cloud Watch 日志组
过期。 如果您想要配置自定义日志保留日, 您需要
指定 logRetention 属性.
import * as logs from 'aws-cdk-lib/aws-logs';
declare const lambdaFunction: lambda.IFunction;
declare const app: App;
const stack = new Stack(app, 'cdk-integ-lambda-bundling');
const integ = new IntegTest(app, 'IntegTest', {
testCases: [stack],
});
const invoke = integ.assertions.invokeFunction({
functionName: lambdaFunction.functionName,
logRetention: logs.RetentionDays.ONE_WEEK,
});
提供者日志级别
默认情况下, 断言提供者 lamda 函数的日志级别设置为 FATAL。 。 。 .
如果您需要调试断言失败, 您可以通过设置增加日志级别
providerLogLevel 编辑 IntegTest 构造 :
declare const app: App;
declare const stack: Stack;
const integ = new IntegTest(app, 'IntegTest', {
testCases: [stack],
providerLogLevel: lambda.ApplicationLogLevel.INFO,
});
调用 AWS API
在这个例子中有一个 StepFunctions 状态机器被执行 然后我们断言执行的结果是成功的.
declare const app: App;
declare const stack: Stack;
declare const sm: IStateMachine;
const testCase = new IntegTest(app, 'IntegTest', {
testCases: [stack],
});
// Start an execution
const start = testCase.assertions.awsApiCall('StepFunctions', 'startExecution', {
stateMachineArn: sm.stateMachineArn,
});
// describe the results of the execution
const describe = testCase.assertions.awsApiCall('StepFunctions', 'describeExecution', {
executionArn: start.getAttString('executionArn'),
});
// assert the results
describe.expect(ExpectedResult.objectLike({
status: 'SUCCEEDED',
}));
链式呼叫
有时可能有必要连锁API呼叫. 由于每个API呼叫都是自己的资源,所有你们
需要做的就是在通话之间增加一个依赖性。 有一个帮助方法 next 这可以使用.
declare const integ: IntegTest;
integ.assertions.awsApiCall('S3', 'putObject', {
Bucket: 'amzn-s3-demo-bucket',
Key: 'my-key',
Body: 'helloWorld',
}).next(integ.assertions.awsApiCall('S3', 'getObject', {
Bucket: 'amzn-s3-demo-bucket',
Key: 'my-key',
}));
等待结果
在作出断言时通常使用的情况是等待一个条件通过。 有时候
你们所争论的是,到判决的时候,还没有判决。 在这些
案件可以同时进行断言,称为 waitForAssertions() 方法.
以上述执行 StepFunts 状态机器为例,视复杂程度而定 国家机器,它可能需要一段时间才能完成.
declare const app: App;
declare const stack: Stack;
declare const sm: IStateMachine;
const testCase = new IntegTest(app, 'IntegTest', {
testCases: [stack],
});
// Start an execution
const start = testCase.assertions.awsApiCall('StepFunctions', 'startExecution', {
stateMachineArn: sm.stateMachineArn,
});
// describe the results of the execution
const describe = testCase.assertions.awsApiCall('StepFunctions', 'describeExecution', {
executionArn: start.getAttString('executionArn'),
}).expect(ExpectedResult.objectLike({
status: 'SUCCEEDED',
})).waitForAssertions();
当你打电话 waitForAssertions() 声称提供者将持续制作 awsApiCall 直至
ExpectedResult 被满足。 也可以控制等待的参数,例如:
declare const testCase: IntegTest;
declare const start: IApiCall;
const describe = testCase.assertions.awsApiCall('StepFunctions', 'describeExecution', {
executionArn: start.getAttString('executionArn'),
}).expect(ExpectedResult.objectLike({
status: 'SUCCEEDED',
})).waitForAssertions({
totalTimeout: Duration.minutes(5),
interval: Duration.seconds(15),
backoffRate: 3,
});