cloud-devops

اختبار AWS CDK في TypeScript: اختبر قبل النشر

١٨ يونيو ٢٠٢٦

AWS CDK Testing in TypeScript: Test Before Deploy 2026

لاختبار وحدة (unit-test) لـ AWS CDK stack في TypeScript، قم بعمل synthesize له إلى قالب CloudFormation باستخدام Template.fromStack() وتحقق من ذلك القالب باستخدام وحدة aws-cdk-lib/assertions و Jest. لا يتم نشر أي شيء ولا يلزم وجود حساب AWS.

ملخص

يضيف هذا الدليل العملي مجموعة اختبار كاملة إلى تطبيق AWS CDK في TypeScript حتى تكتشف أخطاء البنية التحتية قبل تشغيل cdk deploy. ستقوم بإنشاء مشروع باستخدام CDK CLI، وبناء construct صغير لـ IngestPipeline (S3 bucket مشفر ومفعل به خاصية الإصدارات بالإضافة إلى Lambda function)، ثم كتابة fine-grained assertions، و partial matchers، و value captures، و snapshot test، واختبار حوكمة باستخدام Aspects and Annotations — كل ذلك باستخدام Jest 30.4.21 ووحدة assertions الخاصة بـ aws-cdk-lib 2.260.02. تم تنفيذ كل اختبار هنا مقابل synth حقيقي في 18 يونيو 2026: 3 مجموعات، 10 اختبارات، وجميعها ناجحة، بدون بيانات اعتماد AWS وبدون نشر أي شيء. الميزانية الزمنية حوالي 30-40 دقيقة.

ما ستتعلمه

  • كيفية إنشاء مشروع CDK TypeScript مهيأ لـ Jest
  • كيفية كتابة fine-grained assertions مقابل قالب CloudFormation الناتج
  • كيفية استخدام Match للمطابقة الجزئية، والمستقلة عن الترتيب، ومطابقة "يجب أن يكون غائباً"
  • كيفية التقاط القيم المولدة (مثل Ref) والتحقق منها بشكل متقاطع
  • كيفية التحقق من السياسات على مستوى الموارد مثل DeletionPolicy: Retain
  • متى تفيد اختبارات اللقطة (snapshot tests) — وفخ التراجع الذي تخفيه
  • كيفية اختبار قواعد الحوكمة باستخدام Aspects و Annotations
  • كيفية تشغيل المجموعة الكاملة في CI بدون حساب AWS

لماذا نختبر CDK على الإطلاق

يبدو كود CDK مثل التطبيق، لكنه ينتج قالب CloudFormation. خطأ مطبعي في سياسة bucket، أو Lambda ببيئة تشغيل خاطئة، أو construct يسقط التشفير بهدوء لن يفشل في التجميع (compile) — بل سيتم نشره ثم يتسبب في حادثة. يشحن CDK مكتبة assertions تفحص القالب الناتج دون الاتصال بـ AWS، لذا يمكنك إثبات أن الـ stack الخاص بك ينتج الموارد التي تقصدها. هذه هي نفس الفكرة وراء مراجعة خطط Terraform قبل التطبيق، والتي تمت تغطيتها في درس قفل حالة S3 الأصلية في Terraform؛ هنا يحدث التحقق في مشغل الاختبار الخاص بك.

تعمل الاختبارات أدناه في أجزاء من الثانية ولا تحتاج إلى شبكة، لذا فهي تنتمي إلى نفس فحص طلب السحب (pull-request) الذي يشغل اختبارات تطبيقك. يفشل كل منها بصوت عالٍ في اللحظة التي يغير فيها إعادة الهيكلة (refactor) البنية التحتية المولدة، وهو بالضبط ما يريد المراجع معرفته — وليس بعد وصول التغيير بالفعل إلى البيئة.

المتطلبات الأساسية

  • Node.js 22+ (استخدم الصندوق الرملي Node 22.22.3؛ Node 20 أو أحدث يعمل)
  • npm 10+
  • لا يلزم حساب AWS أو بيانات اعتماد — الـ synthesis محلي تماماً
  • الإصدارات المثبتة المستخدمة في الدليل: aws-cdk-lib 2.260.02، aws-cdk (CLI) 2.1128.0، constructs 10.6.0، TypeScript 5.9.3، Jest 30.4.21، ts-jest 29.4.11، @types/jest 30.0.0، @types/node 24.13.2

الخطوة 1: إنشاء مشروع CDK TypeScript

أنشئ مجلداً فارغاً واجعل CDK CLI يولد تطبيق TypeScript. استخدام npx يتجنب التثبيت العالمي.

mkdir ingest-pipeline && cd ingest-pipeline
npx --yes aws-cdk@2.1128.0 init app --language TypeScript

يقوم القالب الأساسي بالفعل بإعداد Jest. يبدو ملف jest.config.js المولد هكذا3:

module.exports = {
  testEnvironment: 'node',
  roots: ['<rootDir>/test'],
  testMatch: ['**/*.test.ts'],
  transform: {
    '^.+\\.tsx?$': 'ts-jest',
  },
  setupFilesAfterEnv: ['aws-cdk-lib/testhelpers/jest-autoclean'],
};

يقوم مساعد jest-autoclean بتسجيل هوك afterAll الذي يحذف مجلدات cloud-assembly المؤقتة التي ينشئها كل synth، بحيث لا تترك عملية تشغيل اختبار كبيرة مجلدات synthesis مؤقتة وراءها4.

تعديل واحد يجعل المخرجات نظيفة. يستخدم ملف tsconfig.json الخاص بالقالب module: "NodeNext"، وتحت هذا الإعداد يطبع ts-jest تحذير hybrid-module TS151002 ما لم يتم تشغيل isolated modules. أضف سطراً واحداً إلى compilerOptions:

{
  "compilerOptions": {
    "isolatedModules": true
  }
}

لتثبيت الإصدارات الدقيقة التي تم التحقق من هذا الدليل بها، قم بتثبيتها صراحة:

npm install aws-cdk-lib@2.260.0 constructs@10.6.0
npm install -D aws-cdk@2.1128.0 jest@30.4.2 ts-jest@29.4.11 \
  @types/jest@30.0.0 @types/node@24.13.2 TypeScript@5.9.3 ts-node@10.9.2

الخطوة 2: تحديد البنية التحتية قيد الاختبار

استبدل lib/ingest-pipeline.ts بـ construct صغير قابل لإعادة الاستخدام: S3 bucket محصن ومعالج Lambda يقرأ منه. هذا هو الكود الذي ستحاسبه اختباراتك.

import { Construct } from 'constructs';
import { Duration, RemovalPolicy } from 'aws-cdk-lib';
import * as s3 from 'aws-cdk-lib/aws-s3';
import * as lambda from 'aws-cdk-lib/aws-lambda';

export interface IngestPipelineProps {
  /** Timeout for the processor function. Defaults to 30 seconds. */
readonly processorTimeout?: Duration;
}

export class IngestPipeline extends Construct {
public readonly bucket: s3.Bucket;
public readonly processor: lambda.Function;

constructor(scope: Construct, id: string, props: IngestPipelineProps = {}) {
super(scope, id);

this.bucket = new s3.Bucket(this, 'Raw', {
      versioned: true,
      encryption: s3.BucketEncryption.S3_MANAGED,
      enforceSSL: true,
      blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
      removalPolicy: RemovalPolicy.RETAIN,
});

this.processor = new lambda.Function(this, 'Processor', {
      runtime: lambda.Runtime.NODEJS_22_X,
      handler: 'index.handler',
      code: lambda.Code.fromInline('exports.handler = async () => ({ ok: true });'),
      timeout: props.processorTimeout ?? Duration.seconds(30),
      environment: { RAW_BUCKET: this.bucket.bucketName },
});

this.bucket.grantRead(this.processor);
}
}

بعض الخيارات تهم الاختبارات أدناه: versioned: true وتشفير S3_MANAGED، و enforceSSL (الذي يضيف سياسة bucket تمنع الوصول غير المشفر بـ TLS)، و RemovalPolicy.RETAIN (الذي يصبح CloudFormation DeletionPolicy)، و grantRead (الذي يولد سياسة IAM). Node 22 هي بيئة تشغيل Lambda الحالية المدعومة حتى أبريل 2027؛ يتوفر أيضاً NODEJS_24_X إذا كنت تفضل الإصدار الأحدث5.

قم بتغليف الـ construct في stack في lib/app-stack.ts:

import { Stack, StackProps } from 'aws-cdk-lib';
import { Construct } from 'constructs';
import { IngestPipeline } from './ingest-pipeline';

export class AppStack extends Stack {
constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id, props);
new IngestPipeline(this, 'Ingest');
}
}

الخطوة 3: كتابة أول Fine-grained Assertion

يتحقق الـ fine-grained assertion من شيء واحد محدد حول القالب المولد — "نوع المورد هذا موجود بهذا العدد من المرات" أو "هذا المورد له هذه الخاصية". إنه العمود الفقري لاختبار CDK والافتراضي الصحيح لاكتشاف التراجعات6. أنشئ test/ingest-pipeline.test.ts:

import { App, Stack, Duration } from 'aws-cdk-lib';
import { Template, Match, Capture } from 'aws-cdk-lib/assertions';
import { IngestPipeline } from '../lib/ingest-pipeline';

function synth(props = {}): Template {
const app = new App();
const stack = new Stack(app, 'TestStack');
new IngestPipeline(stack, 'Ingest', props);
return Template.fromStack(stack);
}

test('creates exactly one encrypted, versioned bucket', () => {
const template = synth();
  template.resourceCountIs('AWS::S3::Bucket', 1);
  template.hasResourceProperties('AWS::S3::Bucket', {
    VersioningConfiguration: { Status: 'Enabled' },
});
});

يقوم Template.fromStack(stack) بعمل synthesis للـ stack في الذاكرة ويعيد كائناً يمكنك الاستعلام عنه. يفشل resourceCountIs إذا كان العدد غير صحيح؛ ويفشل hasResourceProperties إذا لم يكن لأي مورد من هذا النوع مجموعة شاملة (superset) من الخصائص التي تدرجها. قم بتشغيله:

npm test

يجب أن ترى الاختبار ينجح. لم يتم لمس AWS — القالب موجود فقط في الذاكرة.

الخطوة 4: المطابقة الجزئية باستخدام Match

القوالب الحقيقية تتداخل بعمق، ونادراً ما ترغب في التحقق من كل خاصية. تتيح لك أدوات مطابقة Match التحقق فقط من الأجزاء التي تهمك7. أضف هذه الاختبارات إلى نفس الملف:

test('bucket uses S3-managed (AES256) encryption', () => {
synth().hasResourceProperties('AWS::S3::Bucket', {
    BucketEncryption: {
      ServerSideEncryptionConfiguration: Match.arrayWith([
        Match.objectLike({
          ServerSideEncryptionByDefault: { SSEAlgorithm: 'AES256' },
}),
]),
},
});
});

test('enforceSSL adds a deny-insecure-transport policy', () => {
synth().hasResourceProperties('AWS::S3::BucketPolicy', {
    PolicyDocument: {
      Statement: Match.arrayWith([
        Match.objectLike({
          Effect: 'Deny',
          Condition: { Bool: { 'aws:SecureTransport': 'false' } },
}),
]),
},
});
});

test('processor runs Node 22, 30s timeout, no reserved concurrency', () => {
synth().hasResourceProperties('AWS::Lambda::Function', Match.objectLike({
    Runtime: 'nodejs22.x',
    Handler: 'index.handler',
    Timeout: 30,
    ReservedConcurrentExecutions: Match.absent(),
}));
});

ثلاث أدوات مطابقة تقوم بالعمل الشاق. ينجح Match.arrayWith([...]) إذا كانت المصفوفة تحتوي على العناصر المدرجة بأي ترتيب — وهو أمر ضروري لأن ترتيب عبارات CloudFormation غير مضمون. يقوم Match.objectLike({...}) بمطابقة جزئية متكررة، لذا يتم تجاهل المفاتيح الإضافية. ويؤكد Match.absent() أن المفتاح غير موجود — هنا، لإثبات أن الوظيفة ليس لها reserved concurrency. Match.stringLikeRegexp('...') (المستخدم لاحقاً) يطابق سلسلة نصية مقابل تعبير نمطي (regex).

تسمح لك نفس أدوات المطابقة المتداخلة (nested matchers) بالتحقق من IAM المُنشأ. نظرًا لأن الـ construct يستدعي bucket.grantRead(this.processor)، فإن CDK يُصدر سياسة IAM يحتوي بيانها (statement) على السماح بقراءة الكائنات من الـ bucket. يؤكد هذا الاختبار وجود إذن القراءة:

test('grantRead wires an IAM policy allowing s3:GetObject', () => {
  synth().hasResourceProperties('AWS::IAM::Policy', {
    PolicyDocument: {
      Statement: Match.arrayWith([
        Match.objectLike({
          Action: Match.arrayWith(['s3:GetObject*']),
          Effect: 'Allow',
        }),
      ]),
    },
  });
});

التحقق من السياسة المُصنعة (synthesized policy) يبقي الأذونات التي تنشئها الـ constructs الخاصة بك تحت الاختبار: إذا أدت عملية إعادة هيكلة الكود (refactor) إلى إسقاط استدعاء grantRead، فسيختفي بيان s3:GetObject* وسيفشل هذا الاختبار قبل أن يصل التغيير إلى الحساب. (لإثبات أن المنح ليس أوسع من القراءة، يمكنك بالإضافة إلى ذلك التحقق من غياب إجراءات الكتابة مثل s3:PutObject — حيث أن grantReadWrite سيظل يحقق التحقق أعلاه، لأنه يتضمن أيضًا s3:GetObject*.)

الخطوة 5: الربط بين القيم المُنشأة باستخدام Capture

يقوم CDK بتعيين المعرفات المنطقية (logical IDs) وتوصيل المراجع نيابة عنك، لذا لا يمكنك كتابتها بشكل ثابت (hardcode). يقوم Capture بالتقاط القيمة الفعلية المُنشأة أثناء المطابقة حتى تتمكن من التحقق منها بعد ذلك.

test('processor env var points at the created bucket', () => {
  const template = synth();
  const bucketRef = new Capture();

  template.hasResourceProperties('AWS::Lambda::Function', {
    Environment: { Variables: { RAW_BUCKET: bucketRef } },
  });

  // The captured value is a CloudFormation { Ref: <logicalId> } object.
  const ref = bucketRef.asObject().Ref as string;
  expect(ref).toMatch(/^IngestRaw/);
  const buckets = template.findResources('AWS::S3::Bucket');
  expect(Object.keys(buckets)).toContain(ref);
});

بعد تشغيل المطابقة، يعيد bucketRef.asObject() القيمة الحقيقية — { Ref: 'IngestRawC4D239E4' }. نؤكد أن متغير Lambda المسمى RAW_BUCKET يشير إلى معرف منطقي موجود بالفعل في القالب عبر findResources، والذي يعيد خريطة مفاتيحها هي المعرفات المنطقية. ملاحظة هامة: القيمة الملتقطة هي كائن JavaScript عادي، لذا تحقق منها باستخدام مطابقات Jest مثل toMatch، وليس باستخدام مطابق CDK Match.*.

الخطوة 6: التحقق من البيانات الوصفية على مستوى المورد باستخدام hasResource

ينظر hasResourceProperties فقط إلى كتلة Properties. للتحقق من السمات على مستوى المورد مثل DeletionPolicy، استخدم hasResource، الذي يرى المورد بالكامل:

test('bucket is retained on stack deletion', () => {
  synth().hasResource('AWS::S3::Bucket', {
    DeletionPolicy: 'Retain',
    UpdateReplacePolicy: 'Retain',
  });
});

نظرًا لأن الـ construct يضبط RemovalPolicy.RETAIN، يجب على CloudFormation الاحتفاظ بالـ bucket إذا تم حذف الـ stack. يحمي هذا الاختبار من تغيير غير مقصود في السياسة قد يجعل بيانات الإنتاج قابلة للحذف.

الخطوة 7: اختبار اللقطة (Snapshot testing) — ومتى لا يجب الوثوق به

يقوم اختبار اللقطة بتحويل القالب بالكامل إلى صيغة تسلسلية (serialization) ومقارنته بأساس مخزن. أنشئ test/snapshot.test.ts:

import { App, Stack from 'aws-cdk-lib';
import { Template from 'aws-cdk-lib/assertions';
import { IngestPipeline from '../lib/ingest-pipeline';

test('IngestPipeline matches the stored snapshot', () => {
  const app = new App();
  const stack = new Stack(app, 'SnapStack');
  new IngestPipeline(stack, 'Ingest');
  expect(Template.fromStack(stack).toJSON()).toMatchSnapshot();
});

تكتب الجولة الأولى الأساس؛ وتفشل الجولات اللاحقة إذا تغير أي شيء. اللقطات مفيدة حقًا لـ إعادة هيكلة الكود (refactoring): عندما تعيد تشكيل كود CDK الخاص بك ولكنك تنوي عدم تغيير المخرجات، فإن اللقطة غير المتغيرة تثبت ذلك.

الفخ هو التعامل مع اللقطات كاختبارات تراجع (regression tests). توجيهات AWS نفسها صريحة في أن اختبار اللقطة ليس جيدًا في اكتشاف التراجعات، لأنه يقارن القالب بالكامل والتغييرات غير المتعلقة بالكود — مثل تبني construct لأفضل ممارسة جديدة، أو تحديث CDK Toolkit يضيف بيانات وصفية، أو قيمة سياق متغيرة — يمكن أن تغير المخرجات المصنعة وتكسر اللقطة دون أي اختلاف حقيقي في النشر6. استخدم اللقطات لتثبيت عمليات إعادة الهيكلة؛ واستخدم التحققات الدقيقة من الخطوات 3-6 لاكتشاف التراجعات. (كود Lambda المضمن لدينا يبقي هذه اللقطة حتمية؛ بينما Code.fromAsset سيقوم بتضمين بصمة محتوى تتغير اللقطة مع كل تعديل للكود.)

الخطوة 8: اختبار قواعد الحوكمة باستخدام Aspects و Annotations

بالإضافة إلى الموارد الفردية، غالبًا ما تريد سياسة عبر التطبيق بالكامل — على سبيل المثال، "يجب أن يكون لكل S3 bucket خاصية تعقيب الإصدارات (versioning)." يقوم الـ Aspect بزيارة كل construct في الشجرة؛ ونحن نرفق خطأ في وقت التصنيع (synthesis-time) إذا انتهك الـ bucket القاعدة. أنشئ lib/require-versioning.ts:

import { IAspect, Annotations from 'aws-cdk-lib';
import { IConstruct from 'constructs';
import { CfnBucket from 'aws-cdk-lib/aws-s3';

/** Fails validation if any S3 bucket has versioning disabled. */
export class RequireBucketVersioning implements IAspect {
public visit(node: IConstruct): void {
if (node instanceof CfnBucket) {
const versioning = node.versioningConfiguration as
| { status?: string | undefined;
if (!versioning || versioning.status !== 'Enabled') {
Annotations.of(node).addError(
'S3 buckets must have versioning enabled (data-retention policy).',
);
}
}
}
}

يسمح لك التحقق من Annotations API باختبار أن الـ Aspect يعمل بشكل صحيح. أنشئ test/governance.test.ts:

import { App, Stack, Aspects from 'aws-cdk-lib';
import { Annotations, Match from 'aws-cdk-lib/assertions';
import { Bucket from 'aws-cdk-lib/aws-s3';
import { IngestPipeline from '../lib/ingest-pipeline';
import { RequireBucketVersioning from '../lib/require-versioning';

test('flags a bucket created without versioning', () => {
  const app = new App();
  const stack = new Stack(app, 'BadStack');
  new Bucket(stack, 'Unversioned'); // versioning off by default
  Aspects.of(stack).add(new RequireBucketVersioning());

  Annotations.fromStack(stack).hasError(
    '*',
    Match.stringLikeRegexp('versioning enabled'),
  );
});

test('passes a compliant pipeline with no errors', () => {
  const app = new App();
  const stack = new Stack(app, 'GoodStack');
  new IngestPipeline(stack, 'Ingest'); // bucket is versioned
  Aspects.of(stack).add(new RequireBucketVersioning());

  Annotations.fromStack(stack).hasNoError('*', Match.anyValue());
});

يكشف Annotations.fromStack(stack) عن hasError، و hasNoError، و hasWarning، و findError8. الوسيط الأول هو مسار الـ construct (حيث يطابق '*' أي مسار)، والثاني هو مطابق للرسالة. يحول هذا النمط قواعد الامتثال الخاصة بك إلى اختبارات، بحيث يفشل أي تغيير مستقبلي يعطل تعقيب الإصدارات في CI بدلاً من شحنه.

الخطوة 9: تشغيل مجموعة الاختبارات في CI بدون حساب AWS

نظرًا لأن التصنيع (synthesis) محلي، فإن المجموعة الكاملة تعمل على مشغل CI عادي بدون أوراق اعتماد سحابية. أضف .GitHub/workflows/test.yml:

name: cdk-tests
on:
  push:
  pull_request:
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - uses: actions/setup-node@v6
        with:
          node-version: 22
          cache: npm
      - run: npm ci
      - run: npm test

هذا هو خط الأنابيب (pipeline) بالكامل. بمجرد اجتياز هذه الاختبارات، يمكنك توصيل وظيفة نشر تفترض دورًا باستخدام OIDC — راجع دليل نشر AWS بدون مفاتيح باستخدام GitHub Actions OIDC — مع العلم أنه تم التحقق من القالب أولاً. إذا قمت أيضًا بتصدير التتبعات (traces) من التطبيق المنشور، فإن دليل تتبع Node.js باستخدام OpenTelemetry Collector يتناسب جيدًا مع بوابة CI هذه.

التحقق

قم بتشغيل المجموعة الكاملة:

npm test

المخرجات المتوقعة (ستختلف الإصدارات والتوقيت):

PASS test/governance.test.ts
PASS test/snapshot.test.ts
PASS test/ingest-pipeline.test.ts

Test Suites: 3 passed, 3 total
Tests:       10 passed, 10 total
Snapshots:   1 written, 1 total

لتأكيد أن التصنيع نفسه يعمل بدون أوراق اعتماد، قم بإلغاء تعيين أي متغيرات AWS وقم بتصنيع التطبيق:

unset AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_PROFILE
npx cdk synth

يخرج بـ 0 ويكتب cdk.out/AppStack.template.json الذي يحتوي على S3 bucket، وسياسة الـ bucket، ودور وسياسة IAM، ودالة Lambda — وهو دليل على أن نفس القالب الذي تحققت منه اختباراتك هو الذي سيقوم CDK بنشره.

الأخطاء الشائعة

ts-jest[config] TS151002: Using hybrid module kind ... isolatedModules. يستخدم ملف tsconfig.json الخاص بالهيكل module: "NodeNext". اضبط "isolatedModules": true في compilerOptions (الخطوة 1). التحذير بخلاف ذلك غير ضار ولكنه مزعج.

فشل اختبار برسالة "Template has N resources ... but none match as expected." هذه هي رسالة الفشل الدقيقة العادية. يطبع CDK أقرب مورد مطابق ويحدد السطر المخالف، على سبيل المثال !! Expected 25 but received 30 في Timeout. اقرأ المورد المطبوع، وأصلح إما الـ construct أو التحقق، وأعد التشغيل.

كسر اختبار اللقطة بعد npm update. يمكن أن يؤدي ترقية aws-cdk-lib بشكل مشروع إلى تغيير المخرجات المصنعة. تأكد من أن الفرق ناتج فقط عن المكتبة، ثم قم بتحديث الأساس باستخدام npm test -- -u. هذا هو بالضبط السبب في أن اللقطات لا ينبغي أن تكون شبكة اختبار التراجع الخاصة بك (الخطوة 7).

تحقق Capture يرمي خطأ "is not a function" أو لا يطابق أبدًا. استدعِ .asObject() أو .asString() فقط بعد تشغيل استدعاء hasResourceProperties الذي يملأ الالتقاط، وتحقق من النتيجة باستخدام مطابقات Jest — وليس باستخدام مطابق CDK Match.*.

A jest worker process was terminated ... SIGKILL. في المشغلات ذات الذاكرة المحدودة، يمكن أن يؤدي تصنيع CDK بالإضافة إلى مشغلات Jest المتوازية إلى استنفاد ذاكرة الوصول العشوائي (RAM). قم بالتشغيل باستخدام npm test -- --maxWorkers=1 (أو --runInBand). المشغلات الافتراضية جيدة على جهاز عادي.

cdk synth يطبع "NN feature flags are not configured." هذا إشعار معلوماتي من CLI في إصدارات CDK الحديثة، وليس خطأ؛ التصنيع لا يزال ينجح. لا يؤثر ذلك على الاختبارات، التي تستدعي Template.fromStack مباشرة.

الخطوات التالية

لديك الآن مجموعة اختبارات CDK تثبت إعدادات الموارد، وتتحقق من القيم الناتجة، وتثبت عمليات إعادة الهيكلة (refactors) باستخدام لقطة شاشة (snapshot)، وتفرض قواعد الحوكمة — كل ذلك قبل نشر أي شيء. من هنا، يمكنك إضافة تأكيدات (assertions) لسياسات IAM ذات الامتيازات الأقل، واختبار عدة Stacks في تطبيق واحد، ودمج هذه الاختبارات في بوابة النشر الموضحة في دليل تعليمات GitHub Actions OIDC للنشر بدون مفاتيح.

قراءات إضافية

  • AWS — اختبار تطبيقات AWS CDK6
  • AWS — مرجع وحدة aws-cdk-lib.assertions7

Footnotes

  1. Jest on npm (30.4.2). https://www.npmjs.com/package/jest 2

  2. aws-cdk-lib on npm (2.260.0 latest as of 18 June 2026). https://www.npmjs.com/package/aws-cdk-lib 2

  3. Scaffold generated by the aws-cdk CLI cdk init app --language TypeScript (aws-cdk 2.1128.0). https://www.npmjs.com/package/aws-cdk

  4. Generated into jest.config.js by cdk init; implemented in the aws-cdk-lib package as testhelpers/jest-autoclean (registers afterAll(CloudAssembly.cleanupTemporaryDirectories)). https://www.npmjs.com/package/aws-cdk-lib

  5. AWS — Lambda Node.js runtimes (nodejs22.x supported to Apr 2027; nodejs24.x available since Nov 2025). https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html

  6. AWS — Test AWS CDK applications (fine-grained vs snapshot guidance). https://docs.aws.amazon.com/cdk/v2/guide/testing.html 2 3

  7. AWS — aws-cdk-lib.assertions module (Template, Match, Capture). https://docs.aws.amazon.com/cdk/API/v2/docs/aws-cdk-lib.assertions-readme.html 2

  8. AWS — assertions Annotations API (hasError / hasNoError / hasWarning / findError). https://docs.aws.amazon.com/cdk/API/v2/docs/aws-cdk-lib.assertions-readme.html