Dies ist der AWS CDK v2-Entwicklerhandbuch. Das ältere CDK v1 wurde am 1. Juni 2022 in die Wartung aufgenommen und der Support wurde am 1. Juni 2023 eingestellt.
Die vorliegende Übersetzung wurde maschinell erstellt. Im Falle eines Konflikts oder eines Widerspruchs zwischen dieser übersetzten Fassung und der englischen Fassung (einschließlich infolge von Verzögerungen bei der Übersetzung) ist die englische Fassung maßgeblich.
Mit dem AWS CDK kann Ihre Infrastruktur genauso testbar sein wie jeder andere Code, den Sie schreiben. Sie können in der Cloud und lokal testen. In diesem Thema wird beschrieben, wie Sie in der Cloud testen können. Hinweise zu lokalen Tests finden Sie unterTesten und erstellen Sie lokal AWS CDK Anwendungen mit dem AWS SAM CLI. Der Standardansatz zum Testen von AWS CDK Apps verwendet AWS CDK das Assertions-Modul und beliebte Testframeworks wie Jest
Es gibt zwei Kategorien von Tests, die Sie für Apps schreiben können. AWS CDK
-
Präzise Assertionen testen bestimmte Aspekte der generierten AWS CloudFormation Vorlage, z. B. „Diese Ressource hat diese Eigenschaft mit diesem Wert“. Diese Tests können Regressionen erkennen. Sie sind auch nützlich, wenn Sie mithilfe der testgetriebenen Entwicklung neue Funktionen entwickeln. (Sie können zuerst einen Test schreiben und ihn dann bestehen lassen, indem Sie eine korrekte Implementierung schreiben.) Feinkörnige Assertionen sind die am häufigsten verwendeten Tests.
-
Snapshot-Tests testen das synthetisierte AWS CloudFormation Template mit einem zuvor gespeicherten Baseline-Template. Mit Snapshot-Tests können Sie frei umgestalten, da Sie sicher sein können, dass der umgestaltete Code genauso funktioniert wie das Original. Wenn die Änderungen beabsichtigt waren, können Sie eine neue Ausgangsbasis für zukünftige Tests akzeptieren. CDK-Upgrades können jedoch auch dazu führen, dass sich synthetisierte Vorlagen ändern, sodass Sie sich nicht nur auf Snapshots verlassen können, um sicherzustellen, dass Ihre Implementierung korrekt ist.
Anmerkung
Vollständige Versionen der TypeScript Python- und Java-Apps, die in diesem Thema als Beispiele verwendet werden, sind unter verfügbar GitHub
Erste Schritte
Um zu veranschaulichen, wie diese Tests geschrieben werden, erstellen wir einen Stack, der eine AWS Step Functions Zustandsmaschine und eine AWS Lambda Funktion enthält. Die Lambda-Funktion abonniert ein Amazon SNS SNS-Thema und leitet die Nachricht einfach an die Zustandsmaschine weiter.
Erstellen Sie zunächst mit dem CDK-Toolkit ein leeres CDK-Anwendungsprojekt und installieren Sie die benötigten Bibliotheken. Die Konstrukte, die wir verwenden werden, befinden sich alle im CDK-Hauptpaket, was eine Standardabhängigkeit in Projekten ist, die mit dem CDK-Toolkit erstellt wurden. Sie müssen jedoch Ihr Test-Framework installieren.
$
mkdir state-machine && cd state-machine cdk init --language=typescript npm install --save-dev jest @types/jest
Erstellen Sie ein Verzeichnis für Ihre Tests.
$
mkdir test
Bearbeiten Sie die Projektepackage.json
, um NPM mitzuteilen, wie Jest ausgeführt werden soll, und um Jest mitzuteilen, welche Arten von Dateien gesammelt werden sollen. Die erforderlichen Änderungen lauten wie folgt.
-
Fügen Sie dem
scripts
Abschnitt einen neuentest
Schlüssel hinzu -
Fügen Sie Jest und seine Typen zum
devDependencies
Abschnitt hinzu -
Fügen Sie einen neuen Schlüssel der
jest
obersten Ebene mit einer Deklaration hinzumoduleFileExtensions
Diese Änderungen werden in der folgenden Übersicht dargestellt. Platzieren Sie den neuen Text an der unter angegebenen Stellepackage.json
. Die Platzhalter „...“ stehen für vorhandene Teile der Datei, die nicht geändert werden sollten.
{
...
"scripts": {
...
"test": "jest"
},
"devDependencies": {
...
"@types/jest": "^24.0.18",
"jest": "^24.9.0"
},
"jest": {
"moduleFileExtensions": ["js"]
}
}
Der Beispielstapel
Hier ist der Stack, der in diesem Thema getestet wird. Wie bereits beschrieben, enthält es eine Lambda-Funktion und eine Step Functions Functions-Zustandsmaschine und akzeptiert ein oder mehrere Amazon SNS SNS-Themen. Die Lambda-Funktion abonniert die Amazon SNS SNS-Themen und leitet sie an die Zustandsmaschine weiter.
Sie müssen nichts Besonderes tun, um die App testbar zu machen. Tatsächlich unterscheidet sich dieser CDK-Stack in keiner wichtigen Weise von den anderen Beispielstapeln in diesem Handbuch.
Fügen Sie den folgenden Code ein: lib/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);
}
}
}
Wir werden den Haupteinstiegspunkt der App so ändern, dass wir unseren Stack nicht wirklich instanziieren. Wir wollen es nicht versehentlich bereitstellen. Unsere Tests werden eine App und eine Instanz des Stacks zum Testen erstellen. In Kombination mit testgetriebener Entwicklung ist dies eine nützliche Taktik: Stellen Sie sicher, dass der Stack alle Tests besteht, bevor Sie die Bereitstellung aktivieren.
In 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.
Die Lambda-Funktion
Unser Beispielstapel enthält eine Lambda-Funktion, die unsere Zustandsmaschine startet. Wir müssen den Quellcode für diese Funktion bereitstellen, damit das CDK ihn als Teil der Erstellung der Lambda-Funktionsressource bündeln und bereitstellen kann.
-
Erstellen Sie den Ordner
start-state-machine
im Hauptverzeichnis der App. -
Erstellen Sie in diesem Ordner mindestens eine Datei. Sie können beispielsweise den folgenden Code in speichern
start-state-machines/index.js
.exports.handler = async function (event, context) { return 'hello world'; };
Es wird jedoch jede Datei funktionieren, da wir den Stack nicht wirklich bereitstellen werden.
Ausführen von Tests
Als Referenz finden Sie hier die Befehle, mit denen Sie Tests in Ihrer AWS CDK App ausführen. Dies sind dieselben Befehle, mit denen Sie die Tests in jedem Projekt mit demselben Testframework ausführen würden. Fügen Sie bei Sprachen, die einen Build-Schritt erfordern, diesen hinzu, um sicherzustellen, dass Ihre Tests kompiliert wurden.
$
tsc && npm test
Fein abgestufte Behauptungen
Der erste Schritt zum Testen eines Stacks mit feinkörnigen Assertionen besteht darin, den Stack zu synthetisieren, da wir Assertionen anhand der generierten Vorlage schreiben. AWS CloudFormation
Unser StateMachineStackStack
verlangt, dass wir ihm das Amazon SNS SNS-Thema übergeben, damit es an den Zustandsmaschine weitergeleitet wird. In unserem Test erstellen wir also einen separaten Stapel, der das Thema enthält.
Normalerweise können Sie beim Schreiben einer CDK-App das Amazon SNS SNS-Thema im Stack
Konstruktor des Stacks unterordnen und instanziieren. In unserem Test instanziieren wir Stack
direkt, übergeben diesen Stack dann als Gültigkeitsbereich und hängen ihn an den Topic
Stack an. Das ist funktionell äquivalent und weniger ausführlich. Es trägt auch dazu bei, dass Stacks, die nur in Tests verwendet werden, „anders aussehen“ als die Stacks, die Sie bereitstellen möchten.
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);
}
Jetzt können wir behaupten, dass die Lambda-Funktion und das Amazon SNS SNS-Abonnement erstellt wurden.
// 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);
Unser Lambda-Funktionstest bestätigt, dass zwei bestimmte Eigenschaften der Funktionsressource spezifische Werte haben. Standardmäßig führt die hasResourceProperties
Methode eine teilweise Übereinstimmung der Eigenschaften der Ressource durch, wie sie in der synthetisierten CloudFormation Vorlage angegeben sind. Dieser Test setzt voraus, dass die angegebenen Eigenschaften vorhanden sind und die angegebenen Werte haben. Die Ressource kann jedoch auch andere Eigenschaften haben, die nicht getestet wurden.
Unsere Amazon SNS SNS-Behauptung behauptet, dass die synthetisierte Vorlage ein Abonnement enthält, aber nichts über das Abonnement selbst. Wir haben diese Behauptung hauptsächlich aufgenommen, um zu veranschaulichen, wie die Anzahl der Ressourcen bestätigt werden kann. Die Template
Klasse bietet spezifischere Methoden, um Assertions für die Mapping
Abschnitte Resources
Outputs
, und der CloudFormation Vorlage zu schreiben.
Matcher
Das standardmäßige Verhalten bei teilweisem Abgleich von hasResourceProperties
kann mithilfe von Matchern aus der Match
Klasse geändert werden.
Die Übereinstimmungen reichen von lenient (Match.anyValue
) bis strikt (). Match.objectEquals
Sie können verschachtelt werden, um unterschiedliche Abgleichmethoden auf verschiedene Teile der Ressourceneigenschaften anzuwenden. Mithilfe von Match.objectEquals
und Match.anyValue
zusammen können wir beispielsweise die IAM-Rolle der Zustandsmaschine umfassender testen, ohne dass spezifische Werte für Eigenschaften erforderlich sind, die sich ändern könnten.
// 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"],
],
},
},
},
],
},
})
);
Viele CloudFormation Ressourcen enthalten serialisierte JSON-Objekte, die als Zeichenketten dargestellt werden. Der Match.serializedJson()
Matcher kann verwendet werden, um Eigenschaften in diesem JSON abzugleichen.
Beispielsweise werden Step Functions Functions-Zustandsmaschinen mithilfe einer Zeichenfolge in der JSON-basierten Amazon States-Sprache definiert. Damit stellen wir sicherMatch.serializedJson()
, dass unser Ausgangsstatus der einzige Schritt ist. Auch hier verwenden wir verschachtelte Matcher, um verschiedene Arten von Matching auf verschiedene Teile des Objekts anzuwenden.
// 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(),
},
},
})
),
});
Erfassen
Es ist oft nützlich, Eigenschaften zu testen, um sicherzustellen, dass sie bestimmten Formaten entsprechen oder denselben Wert wie eine andere Eigenschaft haben, ohne ihre genauen Werte im Voraus kennen zu müssen. Das assertions
Modul bietet diese Funktion in seiner Capture
Klasse.
Durch die Angabe einer Capture
Instanz anstelle eines Werts in hasResourceProperties
wird dieser Wert im Capture
Objekt beibehalten. Der tatsächlich erfasste Wert kann mit den as
Methoden des Objekts, einschließlichasNumber()
, und asString()
asObject
, abgerufen und einem Test unterzogen werden. Verwenden Sie Capture
ihn zusammen mit einem Matcher, um die genaue Position des zu erfassenden Werts in den Eigenschaften der Ressource, einschließlich serialisierter JSON-Eigenschaften, anzugeben.
Das folgende Beispiel testet, ob der Startstatus unserer Zustandsmaschine einen Namen hat, der mit beginnt. Start
Es testet auch, ob dieser Status in der Liste der Zustände in der Maschine vorhanden ist.
// 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());
Snapshot-Tests
Beim Snapshot-Testen vergleichen Sie die gesamte synthetisierte CloudFormation Vorlage mit einer zuvor gespeicherten Baseline-Vorlage (oft als „Master“ bezeichnet). Im Gegensatz zu feinkörnigen Behauptungen sind Snapshot-Tests nicht nützlich, um Regressionen abzufangen. Das liegt daran, dass Snapshot-Tests für die gesamte Vorlage gelten und Dinge außer Codeänderungen zu kleinen (oder not-so-small) Unterschieden in den Syntheseergebnissen führen können. Diese Änderungen wirken sich möglicherweise nicht einmal auf Ihre Bereitstellung aus, führen aber dennoch dazu, dass ein Snapshot-Test fehlschlägt.
Sie könnten beispielsweise ein CDK-Konstrukt aktualisieren, um eine neue bewährte Methode einzubeziehen, die zu Änderungen an den synthetisierten Ressourcen oder deren Organisation führen kann. Alternativ könnten Sie das CDK Toolkit auf eine Version aktualisieren, die zusätzliche Metadaten meldet. Änderungen an den Kontextwerten können sich auch auf die synthetisierte Vorlage auswirken.
Snapshot-Tests können jedoch beim Refactoring eine große Hilfe sein, solange Sie alle anderen Faktoren, die sich auf das synthetisierte Template auswirken könnten, konstant halten. Sie werden sofort wissen, ob eine Änderung, die Sie vorgenommen haben, die Vorlage unbeabsichtigt verändert hat. Wenn die Änderung beabsichtigt ist, akzeptieren Sie einfach die neue Vorlage als Grundlage.
Wenn wir zum Beispiel dieses DeadLetterQueue
Konstrukt haben:
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(),
});
}
}
Wir können es so testen:
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();
});
});
Tipps für Tests
Denken Sie daran, dass Ihre Tests genauso lange gültig sind wie der Code, den sie testen, und sie werden genauso oft gelesen und geändert. Daher lohnt es sich, sich einen Moment Zeit zu nehmen, um zu überlegen, wie man sie am besten schreibt.
Kopieren Sie keine Setup-Zeilen oder allgemeine Behauptungen und fügen Sie sie nicht ein. Refaktorieren Sie diese Logik stattdessen in Fixtures oder Hilfsfunktionen. Verwenden Sie aussagekräftige Namen, die widerspiegeln, was jeder Test tatsächlich testet.
Versuchen Sie nicht, in einem Test zu viel zu tun. Vorzugsweise sollte ein Test nur ein Verhalten testen. Wenn Sie dieses Verhalten versehentlich brechen, sollte genau ein Test fehlschlagen, und der Name des Tests sollte Ihnen sagen, was fehlgeschlagen ist. Dies ist jedoch eher ein Ideal, das angestrebt werden sollte. Manchmal werden Sie unweigerlich (oder versehentlich) Tests schreiben, die mehr als ein Verhalten testen. Snapshot-Tests sind aus Gründen, die wir bereits beschrieben haben, besonders anfällig für dieses Problem. Verwenden Sie sie daher sparsam.