Testing CDK Applications in Any Language

海外精选
海外精选的内容汇集了全球优质的亚马逊云科技相关技术内容。同时,内容中提到的“AWS” 是 “Amazon Web Services” 的缩写,在此网站不作为商标展示。
0
0
{"value":"The [AWS Cloud Development Kit (AWS CDK)](https://aws.amazon.com/cdk/) is an open source software development framework to define your cloud application resources using familiar programming languages. Because the AWS CDK enables you to define your infrastructure in regular programming languages, you can also write automated unit tests for your infrastructure code, just like you do for your application code. Testing is an essential element to highly effective DevOps practices, and testing your infrastructure code provides benefits such as ensuring that you will create exactly the resources you expect in the AWS cloud and helping to prevent regressions from being introduced to your infrastructure.\n\nToday, I am happy to announce the [assertions module](https://docs.aws.amazon.com/cdk/api/latest/docs/assertions-readme.html) for the AWS Cloud Development Kit, a set of APIs designed to help you write unit tests against your CDK applications, with a focus on CloudFormation templates.\n\n### **Cross-Language Support**\n\nA [previous AWS blog post explains](https://aws.amazon.com/blogs/developer/testing-infrastructure-with-the-aws-cloud-development-kit-cdk/) how to write tests for your infrastructure constructs using the ```assert``` module, which is available only for JavaScript and TypeScript.\n\nSimilar to the ```assert``` module, the new CDK assertions module provides a robust set of APIs to precisely verify the CloudFormation templates synthesized by your CDK app. Additionally, the ```assertions``` module is available for every language supported by the CDK.\n\nWhile the new assertions module supports every language that is supported by the CDK, this snippets in this article will be written in Python. However, the full source code for these examples is [available on GitHub](https://github.com/cdklabs/aws-cdk-testing-examples), and contains equivalent code written in TypeScript, Java, and Python.\n\n- If you are currently using the ```assert``` module and wish to migrate, you can find a [guide on that in our GitHub repository](https://github.com/aws/aws-cdk/blob/master/packages/@aws-cdk/assertions/MIGRATING.md).\n\n### **Fine-grained Assertions**\n\nThe ```assertions``` module provides several tools to both assert that certain parts of a template matches given objects and to retrieve certain parts of of a template. Using these tools, we can assert that resources with a given type and properties exist, assert that certain outputs exist, and assert that a template has a given number of resources.\n\nLet’s assume you have a stack that creates an [AWS Step Functions](https://aws.amazon.com/step-functions) state machine and an [AWS Lambda](https://aws.amazon.com/lambda) function. The Lambda function is subscribed to an [Amazon SNS](https://aws.amazon.com/sns) topic and simply forwards the message to the state machine:\n\nPython\n\n```\nfrom typing import List\n\nfrom aws_cdk import aws_lambda as lambda_\nfrom aws_cdk import aws_sns as sns\nfrom aws_cdk import aws_sns_subscriptions as sns_subscriptions\nfrom aws_cdk import aws_stepfunctions as sfn\nfrom aws_cdk import core as cdk\n\n\nclass ProcessorStack(cdk.Stack):\n def __init__(\n self,\n scope: cdk.Construct,\n construct_id: str,\n *,\n topics: List[sns.Topic],\n **kwargs\n ) -> None:\n super().__init__(scope, construct_id, **kwargs)\n\n # In the future this state machine will do some work...\n state_machine = sfn.StateMachine(\n self, \"StateMachine\", definition=sfn.Pass(self, \"StartState\")\n )\n\n # This Lambda function starts the state machine.\n func = lambda_.Function(\n self,\n \"LambdaFunction\",\n runtime=lambda_.Runtime.NODEJS_14_X,\n handler=\"handler\",\n code=lambda_.Code.from_asset(\"./start-state-machine\"),\n environment={\n \"STATE_MACHINE_ARN\": state_machine.state_machine_arn,\n },\n )\n state_machine.grant_start_execution(func)\n\n subscription = sns_subscriptions.LambdaSubscription(func)\n for topic in topics:\n topic.add_subscription(subscription)\n```\n\nHow do you test this stack with fine-grained assertions? First, start by creating the ```ProcessorStack``` and synthesizing it into a CloudFormation template. Once you have synthesized your stack into a Template, you can assert on it. You’ll notice that every test using the ```assertions``` module starts with first creating a stack and synthesizing it.\n\nAdditionally, since this stack relies on cross-stack references, you can create a stack for the referenced resources to live in and pass those resources to the```ProcessorStack```:\n\nPython\n\n```\nfrom aws_cdk import aws_sns as sns\nfrom aws_cdk import core as cdk\nfrom aws_cdk.assertions import Template\n\nfrom app.processor_stack import ProcessorStack\n\n\ndef test_synthesizes_properly():\n app = cdk.App()\n\n # Since the ProcessorStack consumes resources from a separate stack\n # (cross-stack references), we create a stack for our SNS topics to live\n # in here. These topics can then be passed to the ProcessorStack later,\n # creating a cross-stack reference.\n topics_stack = cdk.Stack(app, \"TopicsStack\")\n\n # Create the topic the stack we're testing will reference.\n topics = [sns.Topic(topics_stack, \"Topic1\")]\n\n # Create the ProcessorStack.\n processor_stack = ProcessorStack(\n app, \"ProcessorStack\", topics=topics # Cross-stack reference\n )\n\n # Prepare the stack for assertions.\n template = Template.from_stack(processor_stack)\n\n```\n\n\nNow you can assert that the Lambda function and a subscription were created:\n\nPython\n\n ```\n# ...\n\n # Assert it creates the function with the correct properties...\n template.has_resource_properties(\n \"AWS::Lambda::Function\",\n {\n \"Handler\": \"handler\",\n \"Runtime\": \"nodejs14.x\",\n },\n )\n\n # Creates the subscription...\n template.resource_count_is(\"AWS::SNS::Subscription\", 1)\n\n # ...\n```\n\nThe ```has_resource_properties()``` method allows you to assert that the template has a resource of the given type with the given properties. This example asserts that each of the expected resources exist and they are configured with the specific properties defined in the tests. There are also many other [methods on the Template class](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_assertions.Template.html#methods) which can be used to verify the ```Resources```, ```Outputs```, and ```Mappings``` sections of the CloudFormation template.\n\n### **Matchers**\n\nLooking at the assertions in the previous example, you will notice that the tested properties are all defined as literal values. However, the methods in the ```assertions``` module also accept special matchers which allow you to define partial or special pattern matching during template assertions. There are several matchers built into the ```assertions``` module, with the full list documented in the [assertions API reference](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_assertions.Match.html). For example, the ```Match.object_like()``` method, checks that the expected values (the properties passed to ```has_resource_properties()```) are a subset of the target values (the properties on the synthesized resource). The ```Match.object_like()``` matcher is commonly used to prevent tests from failing when additional (possibly unrelated) properties are introduced by the CDK construct library.\n\nThe next examples show you how to use various matchers in your unit tests. The ```Match.object_equals()``` matcher checks that the expected values are exactly equal to the target values, rather than a subset. On the other hand, the ```Match.any_value()``` matcher allows the assertion to pass no matter what the target value is. Using a combination of these two matchers, you can fully assert on the state machine’s IAM role:\n\nPython\n\n ```\nfrom aws_cdk.assertions import Match\n\n # ...\n\n # Fully assert on the state machine's IAM role with matchers.\n template.has_resource_properties(\n \"AWS::IAM::Role\",\n Match.object_equals(\n {\n \"AssumeRolePolicyDocument\": {\n \"Version\": \"2012-10-17\",\n \"Statement\": [\n {\n \"Action\": \"sts:AssumeRole\",\n \"Effect\": \"Allow\",\n \"Principal\": {\n \"Service\": {\n \"Fn::Join\": [\n \"\",\n [\n \"states.\",\n Match.any_value(),\n \".amazonaws.com\",\n ],\n ],\n },\n },\n },\n ],\n },\n }\n ),\n )\n```\n\n\n\nIn the next example, assertions for the Step Functions state machine are included in the unit tests. State machines are defined by strings written in the Amazon State Languages (based on JSON). While this is a great way to express state machines, it is difficult to test without any tools to help out. Fortunately, the ```assertions``` module provides a matcher,```Match.serialized_json()```, which JSON-deserializes the target string and matches it against the expected value. You can even nest matchers inside of a ```Match.serialized_json()``` matcher!\n\nThe following example uses ```Match.serialized_json()``` to assert on the state machine’s definition:\n\nPython\n\n```\n# ...\n\n # Assert on the state machine's definition with the serialized_json matcher.\n template.has_resource_properties(\n \"AWS::StepFunctions::StateMachine\",\n {\n \"DefinitionString\": Match.serialized_json(\n # Match.object_equals() is used implicitly, but we use it explicitly\n # here for extra clarity.\n Match.object_equals(\n {\n \"StartAt\": \"StartState\",\n \"States\": {\n \"StartState\": {\n \"Type\": \"Pass\",\n \"End\": True,\n # Make sure this state doesn't provide a next state --\n # we can't provide both Next and set End to true.\n \"Next\": Match.absent(),\n },\n },\n }\n )\n ),\n },\n )\n```\n\n### **Capturing**\n\nThe Capture API of the ```assertions``` module allows you to retrieve values the ```assertions``` module encounters when it is matching and perform your own assertions on those values later. You create a Capture object, use it in your assertion just as if it was any other matcher, and then retrieve its value with the relevant ```as_x()``` method (for example, ```as_string()``` or ```as_object()```).\n\nThis snippet uses a combinations of Captures and the ```Match.serialized_json()``` matcher to assert that the name of the start state of a state machine starts with “Start” and that the start state is actually present within the list of states in the machine, doing some more rudimentary validation on the state machine definition:\n\nPython\n\n ```\nimport re\n\n from aws_cdk.assertions import Capture\n\n # ...\n\n # Capture some data from the state machine's definition.\n start_at_capture = Capture()\n states_capture = Capture()\n template.has_resource_properties(\n \"AWS::StepFunctions::StateMachine\",\n {\n \"DefinitionString\": Match.serialized_json(\n Match.object_like(\n {\n \"StartAt\": start_at_capture,\n \"States\": states_capture,\n }\n )\n ),\n },\n )\n\n # Assert that the start state starts with \"Start\".\n assert re.match(\"^Start\", start_at_capture.as_string())\n\n # Assert that the start state actually exists in the states object of the\n # state machine definition.\n assert start_at_capture.as_string() in states_capture.as_object()\n```\n\n### **Snapshot Testing**\n\nAnother way to test your CDK applications is using snapshot tests. Snapshot tests take a snapshot of an object the first time they run. This snapshot is committed to version control, and every time the test is run after that, the object is compared to the snapshot. If the snapshot matches the object, the assertion passes. If the snapshot does not match, the assertion fails.\n\nSnapshot testing, unlike standard unit testing, is not a mechanism to detect regressions. The CloudFormation template produced during the synthesis of an AWS CDK app is influenced by both the CDK application code as well as the CDK framework. In some cases, the synthesized template can change when the version of the CDK framework is upgraded. This typically happens when a new best practice gets incorporated into the CDK. For this reason, snapshot testing is best used as a mechanism to alert you when anything at all changes in your CDK stacks. Snapshot testing will make these changes visible to you early.\n\nRefactoring your CDK code is another good use of snapshot testing — you don’t want anything to change while you’re refactoring, and snapshot tests will clearly show you when that happens. For almost all other use cases, fine-grained assertions are a better tool.\n\nYou can employ snapshot testing with the ```assertions``` module by first synthesizing the stack into a CloudFormation template, converting the entire template to an object (in Java, a Map, in Python, a dict), and then using our test framework’s snapshot testing functionalities to assert that the template matches that snapshot.\n\nPython\n\n```\nfrom aws_cdk import aws_sns as sns\nfrom aws_cdk import core as cdk\nfrom aws_cdk.assertions import Template\n\nfrom app.processor_stack import ProcessorStack\n\n\n# The snapshot parameter is injected by Pytest -- it's a fixture provided by\n# syrupy, the snapshot testing library we're using:\n# https://docs.pytest.org/en/stable/fixture.html\ndef test_matches_snapshot(snapshot):\n # Set up the app and resources in the other stack.\n app = cdk.App()\n topics_stack = cdk.Stack(app, \"TopicsStack\")\n topics = [sns.Topic(topics_stack, \"Topic1\")]\n\n # Create the ProcessorStack.\n processor_stack = ProcessorStack(\n app, \"ProcessorStack\", topics=topics # Cross-stack reference\n )\n\n # Prepare the stack for assertions.\n template = Template.from_stack(processor_stack)\n\n assert template.to_json() == snapshot\n\n```\n\n\n### **Testing Constructs**\n\nWith the ```assertions``` module, constructs can be tested similarly to stacks. Since there’s no stack to create, the difference is that we create a stack that holds the construct to be tested.\n\nFor example, if we have this ```DeadLetterQueue``` construct (previously used in the [Testing infrastructure with the AWS Cloud Development Kit blog post](https://aws.amazon.com/blogs/developer/testing-infrastructure-with-the-aws-cloud-development-kit-cdk/)):\n\nPython\n\n```\nfrom aws_cdk import aws_cloudwatch as cloudwatch\nfrom aws_cdk import aws_sqs as sqs\nfrom aws_cdk import core as cdk\n\nclass DeadLetterQueue(sqs.Queue):\n def __init__(self, scope: cdk.Construct, id: str):\n super().__init__(scope, id)\n\n self.messages_in_queue_alarm = cloudwatch.Alarm(\n self,\n \"Alarm\",\n alarm_description=\"There are messages in the Dead Letter Queue.\",\n evaluation_periods=1,\n threshold=1,\n metric=self.metric_approximate_number_of_messages_visible(),\n )\n```\nYou can test it like this:\n\nPython\n\n```\nfrom aws_cdk import core as cdk\nfrom aws_cdk.assertions import Match, Template\n\nfrom app.dead_letter_queue import DeadLetterQueue\n\ndef test_creates_alarm():\n stack = cdk.Stack()\n DeadLetterQueue(stack, \"DeadLetterQueue\")\n\n template = Template.from_stack(stack)\n template.has_resource_properties(\n \"AWS::CloudWatch::Alarm\",\n {\n \"Namespace\": \"AWS/SQS\",\n \"MetricName\": \"ApproximateNumberOfMessagesVisible\",\n \"Dimensions\": [\n {\n \"Name\": \"QueueName\",\n \"Value\": Match.any_value(),\n },\n ],\n },\n )\n```\n\n\n### **Summary**\n\nTesting is important to ensure that your code does what you expect and prevent regressions and unexpected changes when making changes. The new AWS CDK ```assertions``` module provides new and more powerful ways to test your infrastructure as code, especially if you are developing in a language where the ```assert``` module isn’t available. For more information on the ```assertions``` module, refer to the [API reference](https://docs.aws.amazon.com/cdk/api/latest/docs/assertions-readme.html). As always, we welcome bug reports, feature requests, and pull requests on the [aws-cdk GitHub repository](https://github.com/aws/aws-cdk).","render":"<p>The <a href=\"https://aws.amazon.com/cdk/\" target=\"_blank\">AWS Cloud Development Kit (AWS CDK)</a> is an open source software development framework to define your cloud application resources using familiar programming languages. Because the AWS CDK enables you to define your infrastructure in regular programming languages, you can also write automated unit tests for your infrastructure code, just like you do for your application code. Testing is an essential element to highly effective DevOps practices, and testing your infrastructure code provides benefits such as ensuring that you will create exactly the resources you expect in the AWS cloud and helping to prevent regressions from being introduced to your infrastructure.</p>\n<p>Today, I am happy to announce the <a href=\"https://docs.aws.amazon.com/cdk/api/latest/docs/assertions-readme.html\" target=\"_blank\">assertions module</a> for the AWS Cloud Development Kit, a set of APIs designed to help you write unit tests against your CDK applications, with a focus on CloudFormation templates.</p>\n<h3><a id=\"CrossLanguage_Support_4\"></a><strong>Cross-Language Support</strong></h3>\n<p>A <a href=\"https://aws.amazon.com/blogs/developer/testing-infrastructure-with-the-aws-cloud-development-kit-cdk/\" target=\"_blank\">previous AWS blog post explains</a> how to write tests for your infrastructure constructs using the <code>assert</code> module, which is available only for JavaScript and TypeScript.</p>\n<p>Similar to the <code>assert</code> module, the new CDK assertions module provides a robust set of APIs to precisely verify the CloudFormation templates synthesized by your CDK app. Additionally, the <code>assertions</code> module is available for every language supported by the CDK.</p>\n<p>While the new assertions module supports every language that is supported by the CDK, this snippets in this article will be written in Python. However, the full source code for these examples is <a href=\"https://github.com/cdklabs/aws-cdk-testing-examples\" target=\"_blank\">available on GitHub</a>, and contains equivalent code written in TypeScript, Java, and Python.</p>\n<ul>\n<li>If you are currently using the <code>assert</code> module and wish to migrate, you can find a <a href=\"https://github.com/aws/aws-cdk/blob/master/packages/@aws-cdk/assertions/MIGRATING.md\" target=\"_blank\">guide on that in our GitHub repository</a>.</li>\n</ul>\n<h3><a id=\"Finegrained_Assertions_14\"></a><strong>Fine-grained Assertions</strong></h3>\n<p>The <code>assertions</code> module provides several tools to both assert that certain parts of a template matches given objects and to retrieve certain parts of of a template. Using these tools, we can assert that resources with a given type and properties exist, assert that certain outputs exist, and assert that a template has a given number of resources.</p>\n<p>Let’s assume you have a stack that creates an <a href=\"https://aws.amazon.com/step-functions\" target=\"_blank\">AWS Step Functions</a> state machine and an <a href=\"https://aws.amazon.com/lambda\" target=\"_blank\">AWS Lambda</a> function. The Lambda function is subscribed to an <a href=\"https://aws.amazon.com/sns\" target=\"_blank\">Amazon SNS</a> topic and simply forwards the message to the state machine:</p>\n<p>Python</p>\n<pre><code class=\"lang-\">from typing import List\n\nfrom aws_cdk import aws_lambda as lambda_\nfrom aws_cdk import aws_sns as sns\nfrom aws_cdk import aws_sns_subscriptions as sns_subscriptions\nfrom aws_cdk import aws_stepfunctions as sfn\nfrom aws_cdk import core as cdk\n\n\nclass ProcessorStack(cdk.Stack):\n def __init__(\n self,\n scope: cdk.Construct,\n construct_id: str,\n *,\n topics: List[sns.Topic],\n **kwargs\n ) -&gt; None:\n super().__init__(scope, construct_id, **kwargs)\n\n # In the future this state machine will do some work...\n state_machine = sfn.StateMachine(\n self, &quot;StateMachine&quot;, definition=sfn.Pass(self, &quot;StartState&quot;)\n )\n\n # This Lambda function starts the state machine.\n func = lambda_.Function(\n self,\n &quot;LambdaFunction&quot;,\n runtime=lambda_.Runtime.NODEJS_14_X,\n handler=&quot;handler&quot;,\n code=lambda_.Code.from_asset(&quot;./start-state-machine&quot;),\n environment={\n &quot;STATE_MACHINE_ARN&quot;: state_machine.state_machine_arn,\n },\n )\n state_machine.grant_start_execution(func)\n\n subscription = sns_subscriptions.LambdaSubscription(func)\n for topic in topics:\n topic.add_subscription(subscription)\n</code></pre>\n<p>How do you test this stack with fine-grained assertions? First, start by creating the <code>ProcessorStack</code> and synthesizing it into a CloudFormation template. Once you have synthesized your stack into a Template, you can assert on it. You’ll notice that every test using the <code>assertions</code> module starts with first creating a stack and synthesizing it.</p>\n<p>Additionally, since this stack relies on cross-stack references, you can create a stack for the referenced resources to live in and pass those resources to the<code>ProcessorStack</code>:</p>\n<p>Python</p>\n<pre><code class=\"lang-\">from aws_cdk import aws_sns as sns\nfrom aws_cdk import core as cdk\nfrom aws_cdk.assertions import Template\n\nfrom app.processor_stack import ProcessorStack\n\n\ndef test_synthesizes_properly():\n app = cdk.App()\n\n # Since the ProcessorStack consumes resources from a separate stack\n # (cross-stack references), we create a stack for our SNS topics to live\n # in here. These topics can then be passed to the ProcessorStack later,\n # creating a cross-stack reference.\n topics_stack = cdk.Stack(app, &quot;TopicsStack&quot;)\n\n # Create the topic the stack we're testing will reference.\n topics = [sns.Topic(topics_stack, &quot;Topic1&quot;)]\n\n # Create the ProcessorStack.\n processor_stack = ProcessorStack(\n app, &quot;ProcessorStack&quot;, topics=topics # Cross-stack reference\n )\n\n # Prepare the stack for assertions.\n template = Template.from_stack(processor_stack)\n\n</code></pre>\n<p>Now you can assert that the Lambda function and a subscription were created:</p>\n<p>Python</p>\n<pre><code class=\"lang-\"># ...\n\n # Assert it creates the function with the correct properties...\n template.has_resource_properties(\n &quot;AWS::Lambda::Function&quot;,\n {\n &quot;Handler&quot;: &quot;handler&quot;,\n &quot;Runtime&quot;: &quot;nodejs14.x&quot;,\n },\n )\n\n # Creates the subscription...\n template.resource_count_is(&quot;AWS::SNS::Subscription&quot;, 1)\n\n # ...\n</code></pre>\n<p>The <code>has_resource_properties()</code> method allows you to assert that the template has a resource of the given type with the given properties. This example asserts that each of the expected resources exist and they are configured with the specific properties defined in the tests. There are also many other <a href=\"https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_assertions.Template.html#methods\" target=\"_blank\">methods on the Template class</a> which can be used to verify the <code>Resources</code>, <code>Outputs</code>, and <code>Mappings</code> sections of the CloudFormation template.</p>\n<h3><a id=\"Matchers_127\"></a><strong>Matchers</strong></h3>\n<p>Looking at the assertions in the previous example, you will notice that the tested properties are all defined as literal values. However, the methods in the <code>assertions</code> module also accept special matchers which allow you to define partial or special pattern matching during template assertions. There are several matchers built into the <code>assertions</code> module, with the full list documented in the <a href=\"https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_assertions.Match.html\" target=\"_blank\">assertions API reference</a>. For example, the <code>Match.object_like()</code> method, checks that the expected values (the properties passed to <code>has_resource_properties()</code>) are a subset of the target values (the properties on the synthesized resource). The <code>Match.object_like()</code> matcher is commonly used to prevent tests from failing when additional (possibly unrelated) properties are introduced by the CDK construct library.</p>\n<p>The next examples show you how to use various matchers in your unit tests. The <code>Match.object_equals()</code> matcher checks that the expected values are exactly equal to the target values, rather than a subset. On the other hand, the <code>Match.any_value()</code> matcher allows the assertion to pass no matter what the target value is. Using a combination of these two matchers, you can fully assert on the state machine’s IAM role:</p>\n<p>Python</p>\n<pre><code class=\"lang-\">from aws_cdk.assertions import Match\n\n # ...\n\n # Fully assert on the state machine's IAM role with matchers.\n template.has_resource_properties(\n &quot;AWS::IAM::Role&quot;,\n Match.object_equals(\n {\n &quot;AssumeRolePolicyDocument&quot;: {\n &quot;Version&quot;: &quot;2012-10-17&quot;,\n &quot;Statement&quot;: [\n {\n &quot;Action&quot;: &quot;sts:AssumeRole&quot;,\n &quot;Effect&quot;: &quot;Allow&quot;,\n &quot;Principal&quot;: {\n &quot;Service&quot;: {\n &quot;Fn::Join&quot;: [\n &quot;&quot;,\n [\n &quot;states.&quot;,\n Match.any_value(),\n &quot;.amazonaws.com&quot;,\n ],\n ],\n },\n },\n },\n ],\n },\n }\n ),\n )\n</code></pre>\n<p>In the next example, assertions for the Step Functions state machine are included in the unit tests. State machines are defined by strings written in the Amazon State Languages (based on JSON). While this is a great way to express state machines, it is difficult to test without any tools to help out. Fortunately, the <code>assertions</code> module provides a matcher,<code>Match.serialized_json()</code>, which JSON-deserializes the target string and matches it against the expected value. You can even nest matchers inside of a <code>Match.serialized_json()</code> matcher!</p>\n<p>The following example uses <code>Match.serialized_json()</code> to assert on the state machine’s definition:</p>\n<p>Python</p>\n<pre><code class=\"lang-\"># ...\n\n # Assert on the state machine's definition with the serialized_json matcher.\n template.has_resource_properties(\n &quot;AWS::StepFunctions::StateMachine&quot;,\n {\n &quot;DefinitionString&quot;: Match.serialized_json(\n # Match.object_equals() is used implicitly, but we use it explicitly\n # here for extra clarity.\n Match.object_equals(\n {\n &quot;StartAt&quot;: &quot;StartState&quot;,\n &quot;States&quot;: {\n &quot;StartState&quot;: {\n &quot;Type&quot;: &quot;Pass&quot;,\n &quot;End&quot;: True,\n # Make sure this state doesn't provide a next state --\n # we can't provide both Next and set End to true.\n &quot;Next&quot;: Match.absent(),\n },\n },\n }\n )\n ),\n },\n )\n</code></pre>\n<h3><a id=\"Capturing_208\"></a><strong>Capturing</strong></h3>\n<p>The Capture API of the <code>assertions</code> module allows you to retrieve values the <code>assertions</code> module encounters when it is matching and perform your own assertions on those values later. You create a Capture object, use it in your assertion just as if it was any other matcher, and then retrieve its value with the relevant <code>as_x()</code> method (for example, <code>as_string()</code> or <code>as_object()</code>).</p>\n<p>This snippet uses a combinations of Captures and the <code>Match.serialized_json()</code> matcher to assert that the name of the start state of a state machine starts with “Start” and that the start state is actually present within the list of states in the machine, doing some more rudimentary validation on the state machine definition:</p>\n<p>Python</p>\n<pre><code class=\"lang-\">import re\n\n from aws_cdk.assertions import Capture\n\n # ...\n\n # Capture some data from the state machine's definition.\n start_at_capture = Capture()\n states_capture = Capture()\n template.has_resource_properties(\n &quot;AWS::StepFunctions::StateMachine&quot;,\n {\n &quot;DefinitionString&quot;: Match.serialized_json(\n Match.object_like(\n {\n &quot;StartAt&quot;: start_at_capture,\n &quot;States&quot;: states_capture,\n }\n )\n ),\n },\n )\n\n # Assert that the start state starts with &quot;Start&quot;.\n assert re.match(&quot;^Start&quot;, start_at_capture.as_string())\n\n # Assert that the start state actually exists in the states object of the\n # state machine definition.\n assert start_at_capture.as_string() in states_capture.as_object()\n</code></pre>\n<h3><a id=\"Snapshot_Testing_248\"></a><strong>Snapshot Testing</strong></h3>\n<p>Another way to test your CDK applications is using snapshot tests. Snapshot tests take a snapshot of an object the first time they run. This snapshot is committed to version control, and every time the test is run after that, the object is compared to the snapshot. If the snapshot matches the object, the assertion passes. If the snapshot does not match, the assertion fails.</p>\n<p>Snapshot testing, unlike standard unit testing, is not a mechanism to detect regressions. The CloudFormation template produced during the synthesis of an AWS CDK app is influenced by both the CDK application code as well as the CDK framework. In some cases, the synthesized template can change when the version of the CDK framework is upgraded. This typically happens when a new best practice gets incorporated into the CDK. For this reason, snapshot testing is best used as a mechanism to alert you when anything at all changes in your CDK stacks. Snapshot testing will make these changes visible to you early.</p>\n<p>Refactoring your CDK code is another good use of snapshot testing — you don’t want anything to change while you’re refactoring, and snapshot tests will clearly show you when that happens. For almost all other use cases, fine-grained assertions are a better tool.</p>\n<p>You can employ snapshot testing with the <code>assertions</code> module by first synthesizing the stack into a CloudFormation template, converting the entire template to an object (in Java, a Map, in Python, a dict), and then using our test framework’s snapshot testing functionalities to assert that the template matches that snapshot.</p>\n<p>Python</p>\n<pre><code class=\"lang-\">from aws_cdk import aws_sns as sns\nfrom aws_cdk import core as cdk\nfrom aws_cdk.assertions import Template\n\nfrom app.processor_stack import ProcessorStack\n\n\n# The snapshot parameter is injected by Pytest -- it's a fixture provided by\n# syrupy, the snapshot testing library we're using:\n# https://docs.pytest.org/en/stable/fixture.html\ndef test_matches_snapshot(snapshot):\n # Set up the app and resources in the other stack.\n app = cdk.App()\n topics_stack = cdk.Stack(app, &quot;TopicsStack&quot;)\n topics = [sns.Topic(topics_stack, &quot;Topic1&quot;)]\n\n # Create the ProcessorStack.\n processor_stack = ProcessorStack(\n app, &quot;ProcessorStack&quot;, topics=topics # Cross-stack reference\n )\n\n # Prepare the stack for assertions.\n template = Template.from_stack(processor_stack)\n\n assert template.to_json() == snapshot\n\n</code></pre>\n<h3><a id=\"Testing_Constructs_290\"></a><strong>Testing Constructs</strong></h3>\n<p>With the <code>assertions</code> module, constructs can be tested similarly to stacks. Since there’s no stack to create, the difference is that we create a stack that holds the construct to be tested.</p>\n<p>For example, if we have this <code>DeadLetterQueue</code> construct (previously used in the <a href=\"https://aws.amazon.com/blogs/developer/testing-infrastructure-with-the-aws-cloud-development-kit-cdk/\" target=\"_blank\">Testing infrastructure with the AWS Cloud Development Kit blog post</a>):</p>\n<p>Python</p>\n<pre><code class=\"lang-\">from aws_cdk import aws_cloudwatch as cloudwatch\nfrom aws_cdk import aws_sqs as sqs\nfrom aws_cdk import core as cdk\n\nclass DeadLetterQueue(sqs.Queue):\n def __init__(self, scope: cdk.Construct, id: str):\n super().__init__(scope, id)\n\n self.messages_in_queue_alarm = cloudwatch.Alarm(\n self,\n &quot;Alarm&quot;,\n alarm_description=&quot;There are messages in the Dead Letter Queue.&quot;,\n evaluation_periods=1,\n threshold=1,\n metric=self.metric_approximate_number_of_messages_visible(),\n )\n</code></pre>\n<p>You can test it like this:</p>\n<p>Python</p>\n<pre><code class=\"lang-\">from aws_cdk import core as cdk\nfrom aws_cdk.assertions import Match, Template\n\nfrom app.dead_letter_queue import DeadLetterQueue\n\ndef test_creates_alarm():\n stack = cdk.Stack()\n DeadLetterQueue(stack, &quot;DeadLetterQueue&quot;)\n\n template = Template.from_stack(stack)\n template.has_resource_properties(\n &quot;AWS::CloudWatch::Alarm&quot;,\n {\n &quot;Namespace&quot;: &quot;AWS/SQS&quot;,\n &quot;MetricName&quot;: &quot;ApproximateNumberOfMessagesVisible&quot;,\n &quot;Dimensions&quot;: [\n {\n &quot;Name&quot;: &quot;QueueName&quot;,\n &quot;Value&quot;: Match.any_value(),\n },\n ],\n },\n )\n</code></pre>\n<h3><a id=\"Summary_347\"></a><strong>Summary</strong></h3>\n<p>Testing is important to ensure that your code does what you expect and prevent regressions and unexpected changes when making changes. The new AWS CDK <code>assertions</code> module provides new and more powerful ways to test your infrastructure as code, especially if you are developing in a language where the <code>assert</code> module isn’t available. For more information on the <code>assertions</code> module, refer to the <a href=\"https://docs.aws.amazon.com/cdk/api/latest/docs/assertions-readme.html\" target=\"_blank\">API reference</a>. As always, we welcome bug reports, feature requests, and pull requests on the <a href=\"https://github.com/aws/aws-cdk\" target=\"_blank\">aws-cdk GitHub repository</a>.</p>\n"}
目录
亚马逊云科技解决方案 基于行业客户应用场景及技术领域的解决方案
联系亚马逊云科技专家
亚马逊云科技解决方案
基于行业客户应用场景及技术领域的解决方案
联系专家
0
目录
关闭
contact-us