Ini adalah Panduan Pengembang AWS CDK v2. CDK v1 yang lebih lama memasuki pemeliharaan pada 1 Juni 2022 dan mengakhiri dukungan pada 1 Juni 2023.
Terjemahan disediakan oleh mesin penerjemah. Jika konten terjemahan yang diberikan bertentangan dengan versi bahasa Inggris aslinya, utamakan versi bahasa Inggris.
Dengan AWS CDK, infrastruktur Anda dapat diuji seperti kode lain yang Anda tulis. Anda dapat menguji di cloud dan secara lokal. Topik ini membahas cara menguji di cloud. Untuk panduan tentang pengujian lokal lihatUji dan bangun AWS CDK aplikasi secara lokal dengan AWS SAM CLI. Pendekatan standar untuk menguji AWS CDK aplikasi menggunakan modul pernyataan dan kerangka kerja pengujian populer seperti Jest
Ada dua kategori tes yang dapat Anda tulis untuk AWS CDK aplikasi.
-
Pernyataan berbutir halus menguji aspek spesifik dari AWS CloudFormation templat yang dihasilkan, seperti “sumber daya ini memiliki properti ini dengan nilai ini.” Tes ini dapat mendeteksi regresi. Mereka juga berguna saat Anda mengembangkan fitur baru menggunakan pengembangan berbasis tes. (Anda dapat menulis tes terlebih dahulu, kemudian membuatnya lulus dengan menulis implementasi yang benar.) Pernyataan berbutir halus adalah tes yang paling sering digunakan.
-
Tes snapshot menguji template yang disintesis terhadap AWS CloudFormation template dasar yang disimpan sebelumnya. Tes snapshot memungkinkan Anda melakukan refactor secara bebas, karena Anda dapat yakin bahwa kode refactored bekerja persis dengan cara yang sama seperti aslinya. Jika perubahan itu disengaja, Anda dapat menerima baseline baru untuk pengujian masa depan. Namun, peningkatan CDK juga dapat menyebabkan templat yang disintesis berubah, jadi Anda tidak dapat hanya mengandalkan snapshot untuk memastikan implementasi Anda benar.
catatan
Versi lengkap aplikasi TypeScript, Python, dan Java yang digunakan sebagai contoh dalam topik ini tersedia
Memulai
Untuk mengilustrasikan cara menulis tes ini, kita akan membuat tumpukan yang berisi mesin AWS Step Functions status dan AWS Lambda fungsi. Fungsi Lambda berlangganan topik Amazon SNS dan hanya meneruskan pesan ke mesin status.
Pertama, buat proyek aplikasi CDK kosong menggunakan CDK Toolkit dan instal pustaka yang kita perlukan. Konstruksi yang akan kita gunakan semuanya ada dalam paket CDK utama, yang merupakan ketergantungan default dalam proyek yang dibuat dengan CDK Toolkit. Namun, Anda harus menginstal kerangka pengujian Anda.
$
mkdir state-machine && cd state-machine cdk init --language=typescript npm install --save-dev jest @types/jest
Buat direktori untuk pengujian Anda.
$
mkdir test
Edit proyek package.json
untuk memberi tahu NPM cara menjalankan Jest, dan untuk memberi tahu Jest jenis file apa yang harus dikumpulkan. Perubahan yang diperlukan adalah sebagai berikut.
-
Tambahkan
test
kunci baru kescripts
bagian -
Tambahkan Jest dan tipenya ke bagian
devDependencies
-
Tambahkan kunci
jest
tingkat atas baru dengan deklarasimoduleFileExtensions
Perubahan ini ditunjukkan pada garis besar berikut. Tempatkan teks baru di tempat yang ditunjukkanpackage.json
. Placeholder “...” menunjukkan bagian file yang ada yang tidak boleh diubah.
{
...
"scripts": {
...
"test": "jest"
},
"devDependencies": {
...
"@types/jest": "^24.0.18",
"jest": "^24.9.0"
},
"jest": {
"moduleFileExtensions": ["js"]
}
}
Contoh tumpukan
Inilah tumpukan yang akan diuji dalam topik ini. Seperti yang telah kami jelaskan sebelumnya, ini berisi fungsi Lambda dan mesin status Step Functions, dan menerima satu atau beberapa topik Amazon SNS. Fungsi Lambda berlangganan topik Amazon SNS dan meneruskannya ke mesin status.
Anda tidak perlu melakukan sesuatu yang istimewa untuk membuat aplikasi dapat diuji. Faktanya, tumpukan CDK ini tidak berbeda dengan cara yang penting dari tumpukan contoh lainnya dalam Panduan ini.
Tempatkan kode berikut dilib/state-machine-stack.ts
:
import * as cdk from "aws-cdk-lib";
import * as sns from "aws-cdk-lib/aws-sns";
import * as sns_subscriptions from "aws-cdk-lib/aws-sns-subscriptions";
import * as lambda from "aws-cdk-lib/aws-lambda";
import * as sfn from "aws-cdk-lib/aws-stepfunctions";
import { Construct } from "constructs";
export interface StateMachineStackProps extends cdk.StackProps {
readonly topics: sns.Topic[];
}
export class StateMachineStack extends cdk.Stack {
constructor(scope: Construct, id: string, props: StateMachineStackProps) {
super(scope, id, props);
// In the future this state machine will do some work...
const stateMachine = new sfn.StateMachine(this, "StateMachine", {
definition: new sfn.Pass(this, "StartState"),
});
// This Lambda function starts the state machine.
const func = new lambda.Function(this, "LambdaFunction", {
runtime: lambda.Runtime.NODEJS_18_X,
handler: "handler",
code: lambda.Code.fromAsset("./start-state-machine"),
environment: {
STATE_MACHINE_ARN: stateMachine.stateMachineArn,
},
});
stateMachine.grantStartExecution(func);
const subscription = new sns_subscriptions.LambdaSubscription(func);
for (const topic of props.topics) {
topic.addSubscription(subscription);
}
}
}
Kita akan memodifikasi titik masuk utama aplikasi sehingga kita tidak benar-benar membuat instance stack kita. Kami tidak ingin menyebarkannya secara tidak sengaja. Pengujian kami akan membuat aplikasi dan instance tumpukan untuk pengujian. Ini adalah taktik yang berguna bila dikombinasikan dengan pengembangan berbasis tes: pastikan tumpukan melewati semua pengujian sebelum Anda mengaktifkan penerapan.
Dalam bin/state-machine.ts
:
#!/usr/bin/env node
import * as cdk from "aws-cdk-lib";
const app = new cdk.App();
// Stacks are intentionally not created here -- this application isn't meant to
// be deployed.
Fungsi Lambda
Contoh tumpukan kami mencakup fungsi Lambda yang memulai mesin status kami. Kita harus menyediakan kode sumber untuk fungsi ini sehingga CDK dapat menggabungkan dan menyebarkannya sebagai bagian dari pembuatan sumber daya fungsi Lambda.
-
Buat folder
start-state-machine
di direktori utama aplikasi. -
Di folder ini, buat setidaknya satu file. Misalnya, Anda dapat menyimpan kode berikut di
start-state-machines/index.js
.exports.handler = async function (event, context) { return 'hello world'; };
Namun, file apa pun akan berfungsi, karena kami tidak akan benar-benar menyebarkan tumpukan.
Menjalankan tes
Sebagai referensi, berikut adalah perintah yang Anda gunakan untuk menjalankan pengujian di AWS CDK aplikasi Anda. Ini adalah perintah yang sama yang akan Anda gunakan untuk menjalankan pengujian dalam proyek apa pun menggunakan kerangka pengujian yang sama. Untuk bahasa yang memerlukan langkah build, sertakan itu untuk memastikan pengujian Anda telah dikompilasi.
$
tsc && npm test
Pernyataan berbutir halus
Langkah pertama untuk menguji tumpukan dengan pernyataan berbutir halus adalah mensintesis tumpukan, karena kami menulis pernyataan terhadap template yang dihasilkan. AWS CloudFormation
Kami StateMachineStackStack
mengharuskan kami meneruskannya topik Amazon SNS untuk diteruskan ke mesin negara. Jadi dalam pengujian kami, kami akan membuat tumpukan terpisah untuk memuat topik.
Biasanya, saat menulis aplikasi CDK, Anda dapat mensubkelas Stack
dan membuat instance topik Amazon SNS di konstruktor tumpukan. Dalam pengujian kami, kami membuat instance Stack
secara langsung, lalu meneruskan tumpukan ini sebagai ruang lingkup, melampirkannya ke tumpukan. Topic
Ini setara secara fungsional dan kurang bertele-tele. Ini juga membantu membuat tumpukan yang hanya digunakan dalam pengujian “terlihat berbeda” dari tumpukan yang ingin Anda terapkan.
import { Capture, Match, Template } from "aws-cdk-lib/assertions";
import * as cdk from "aws-cdk-lib";
import * as sns from "aws-cdk-lib/aws-sns";
import { StateMachineStack } from "../lib/state-machine-stack";
describe("StateMachineStack", () => {
test("synthesizes the way we expect", () => {
const app = new cdk.App();
// Since the StateMachineStack consumes resources from a separate stack
// (cross-stack references), we create a stack for our SNS topics to live
// in here. These topics can then be passed to the StateMachineStack later,
// creating a cross-stack reference.
const topicsStack = new cdk.Stack(app, "TopicsStack");
// Create the topic the stack we're testing will reference.
const topics = [new sns.Topic(topicsStack, "Topic1", {})];
// Create the StateMachineStack.
const stateMachineStack = new StateMachineStack(app, "StateMachineStack", {
topics: topics, // Cross-stack reference
});
// Prepare the stack for assertions.
const template = Template.fromStack(stateMachineStack);
}
Sekarang kita dapat menegaskan bahwa fungsi Lambda dan langganan Amazon SNS telah dibuat.
// Assert it creates the function with the correct properties...
template.hasResourceProperties("AWS::Lambda::Function", {
Handler: "handler",
Runtime: "nodejs14.x",
});
// Creates the subscription...
template.resourceCountIs("AWS::SNS::Subscription", 1);
Uji fungsi Lambda kami menegaskan bahwa dua properti tertentu dari sumber daya fungsi memiliki nilai tertentu. Secara default, hasResourceProperties
metode melakukan kecocokan sebagian pada properti sumber daya seperti yang diberikan dalam CloudFormation template yang disintesis. Tes ini mensyaratkan bahwa properti yang disediakan ada dan memiliki nilai yang ditentukan, tetapi sumber daya juga dapat memiliki properti lain, yang tidak diuji.
Pernyataan Amazon SNS kami menegaskan bahwa template yang disintesis berisi langganan, tetapi tidak ada tentang langganan itu sendiri. Kami menyertakan pernyataan ini terutama untuk menggambarkan bagaimana menegaskan jumlah sumber daya. Template
Kelas menawarkan metode yang lebih spesifik untuk menulis pernyataan terhadapResources
,Outputs
, dan Mapping
bagian dari template. CloudFormation
Pencocokan
Perilaku pencocokan paral default hasResourceProperties
dapat diubah menggunakan matcher dari kelas. Match
Matcher berkisar dari lunak () hingga ketat (Match.anyValue
). Match.objectEquals
Mereka dapat disarangkan untuk menerapkan metode pencocokan yang berbeda ke berbagai bagian properti sumber daya. Menggunakan Match.objectEquals
dan Match.anyValue
bersama-sama, misalnya, kita dapat menguji peran IAM mesin status lebih lengkap, sementara tidak memerlukan nilai spesifik untuk properti yang dapat berubah.
// Fully assert on the state machine's IAM role with matchers.
template.hasResourceProperties(
"AWS::IAM::Role",
Match.objectEquals({
AssumeRolePolicyDocument: {
Version: "2012-10-17",
Statement: [
{
Action: "sts:AssumeRole",
Effect: "Allow",
Principal: {
Service: {
"Fn::Join": [
"",
["states.", Match.anyValue(), ".amazonaws.com"],
],
},
},
},
],
},
})
);
Banyak CloudFormation sumber daya termasuk objek JSON serial yang direpresentasikan sebagai string. Match.serializedJson()
Matcher dapat digunakan untuk mencocokkan properti di dalam JSON ini.
Misalnya, mesin status Step Functions didefinisikan menggunakan string dalam Amazon States Language berbasis JSON. Kita akan gunakan Match.serializedJson()
untuk memastikan bahwa status awal kita adalah satu-satunya langkah. Sekali lagi, kita akan menggunakan pencocokan bersarang untuk menerapkan berbagai jenis pencocokan ke berbagai bagian objek.
// Assert on the state machine's definition with the Match.serializedJson()
// matcher.
template.hasResourceProperties("AWS::StepFunctions::StateMachine", {
DefinitionString: Match.serializedJson(
// Match.objectEquals() is used implicitly, but we use it explicitly
// here for extra clarity.
Match.objectEquals({
StartAt: "StartState",
States: {
StartState: {
Type: "Pass",
End: true,
// Make sure this state doesn't provide a next state -- we can't
// provide both Next and set End to true.
Next: Match.absent(),
},
},
})
),
});
Menangkap
Seringkali berguna untuk menguji properti untuk memastikan mereka mengikuti format tertentu, atau memiliki nilai yang sama dengan properti lain, tanpa perlu mengetahui nilai pastinya sebelumnya. assertions
Modul ini menyediakan kemampuan ini di Capture
kelasnya.
Dengan menentukan Capture
instance di tempat nilai dihasResourceProperties
, nilai itu dipertahankan dalam objek. Capture
Nilai yang ditangkap sebenarnya dapat diambil menggunakan as
metode objek, termasukasNumber()
,asString()
, danasObject
, dan diuji. Gunakan Capture
dengan matcher untuk menentukan lokasi yang tepat dari nilai yang akan ditangkap dalam properti sumber daya, termasuk properti JSON serial.
Contoh berikut menguji untuk memastikan bahwa status awal mesin status kami memiliki nama yang dimulai denganStart
. Ini juga menguji bahwa keadaan ini ada dalam daftar negara bagian dalam mesin.
// Capture some data from the state machine's definition.
const startAtCapture = new Capture();
const statesCapture = new Capture();
template.hasResourceProperties("AWS::StepFunctions::StateMachine", {
DefinitionString: Match.serializedJson(
Match.objectLike({
StartAt: startAtCapture,
States: statesCapture,
})
),
});
// Assert that the start state starts with "Start".
expect(startAtCapture.asString()).toEqual(expect.stringMatching(/^Start/));
// Assert that the start state actually exists in the states object of the
// state machine definition.
expect(statesCapture.asObject()).toHaveProperty(startAtCapture.asString());
Tes snapshot
Dalam pengujian snapshot, Anda membandingkan seluruh template yang disintesis dengan CloudFormation template baseline yang disimpan sebelumnya (sering disebut “master”). Tidak seperti pernyataan berbutir halus, pengujian snapshot tidak berguna dalam menangkap regresi. Ini karena pengujian snapshot berlaku untuk seluruh template, dan hal-hal selain perubahan kode dapat menyebabkan perbedaan kecil (atau not-so-small) dalam hasil sintesis. Perubahan ini bahkan mungkin tidak memengaruhi penerapan Anda, tetapi masih akan menyebabkan pengujian snapshot gagal.
Misalnya, Anda dapat memperbarui konstruksi CDK untuk menggabungkan praktik terbaik baru, yang dapat menyebabkan perubahan pada sumber daya yang disintesis atau bagaimana mereka diatur. Atau, Anda dapat memperbarui CDK Toolkit ke versi yang melaporkan metadata tambahan. Perubahan nilai konteks juga dapat mempengaruhi template yang disintesis.
Namun, tes snapshot dapat sangat membantu dalam refactoring, selama Anda mempertahankan semua faktor lain yang dapat memengaruhi templat yang disintesis. Anda akan segera tahu jika perubahan yang Anda buat telah mengubah template secara tidak sengaja. Jika perubahan disengaja, cukup terima template baru sebagai baseline.
Misalnya, jika kita memiliki DeadLetterQueue
konstruksi ini:
export class DeadLetterQueue extends sqs.Queue {
public readonly messagesInQueueAlarm: cloudwatch.IAlarm;
constructor(scope: Construct, id: string) {
super(scope, id);
// Add the alarm
this.messagesInQueueAlarm = new cloudwatch.Alarm(this, 'Alarm', {
alarmDescription: 'There are messages in the Dead Letter Queue',
evaluationPeriods: 1,
threshold: 1,
metric: this.metricApproximateNumberOfMessagesVisible(),
});
}
}
Kita bisa mengujinya seperti ini:
import { Match, Template } from "aws-cdk-lib/assertions";
import * as cdk from "aws-cdk-lib";
import { DeadLetterQueue } from "../lib/dead-letter-queue";
describe("DeadLetterQueue", () => {
test("matches the snapshot", () => {
const stack = new cdk.Stack();
new DeadLetterQueue(stack, "DeadLetterQueue");
const template = Template.fromStack(stack);
expect(template.toJSON()).toMatchSnapshot();
});
});
Kiat untuk tes
Ingat, tes Anda akan hidup selama kode yang mereka uji, dan mereka akan dibaca dan dimodifikasi sesering mungkin. Oleh karena itu, perlu waktu sejenak untuk mempertimbangkan cara terbaik untuk menulisnya.
Jangan salin dan tempel baris penyiapan atau pernyataan umum. Sebaliknya, refaktorkan logika ini menjadi perlengkapan atau fungsi pembantu. Gunakan nama baik yang mencerminkan apa yang sebenarnya diuji oleh setiap tes.
Jangan mencoba melakukan terlalu banyak dalam satu tes. Lebih disukai, tes harus menguji satu dan hanya satu perilaku. Jika Anda secara tidak sengaja merusak perilaku itu, tepat satu tes akan gagal, dan nama tes akan memberi tahu Anda apa yang gagal. Namun, ini lebih ideal untuk diperjuangkan; terkadang Anda pasti akan (atau tidak sengaja) menulis tes yang menguji lebih dari satu perilaku. Tes snapshot, untuk alasan yang telah kami jelaskan, terutama rentan terhadap masalah ini, jadi gunakan dengan hemat.