背景
我经常听到人们说,我们应该用代码做TDD。 TDD好多了。但是什么是TDD?为什么会更好?以及如何将其添加到例如CDK中?如果您想知道原因,请继续...
但是,需要CDK的基本知识,因为我不会解释什么是CDK。如果您愿意,请按照AWS提供的研讨会
- cdkworkshop.com
- cdk-advanced.workshop.aws
TDD
因此,TDD代表测试驱动的开发。测试驱动开发(TDD)是一种软件开发方法,在编写实际代码之前强调写作测试。
TDD的主要目标是创建一个清晰,可靠且可维护的代码库。这是通过首先编写测试来实现的,以便开发人员可以确保其代码符合所需的规格和行为。
TDD过程由三个主要步骤组成,通常称为“红色,绿色和重构”:
-
红色(编写失败的测试):在此步骤中,开发人员应在实施代码之前为特定功能或功能编写测试。该测试最初应失败,因为尚未编写相应的代码。
-
绿色(编写代码以通过测试):开发人员然后编写通过测试所需的最小代码。重点是进行测试通行证,而不是创建优化或完整的解决方案。
-
重构(改进代码):一旦测试通过,开发人员可以重构代码以提高其设计,可读性和效率,同时确保测试仍然通过。此步骤对于维护干净可维护的代码库至关重要。
这些步骤在每个功能上都迭代重复,从而涵盖了整个应用程序的全面测试套件。
所以您可能会问什么好处,那么它可以帮助您处理几件事:
-
改进的代码质量:在实际代码之前编写测试鼓励您对所需的行为和设计进行批判性思考,从而导致更清洁,更可靠的代码。
-
更容易的调试:当测试失败时,识别和解决问题要容易得多,因为测试专注于特定功能。
-
更快的开发:通过在开发过程的早期捕获错误,TDD有助于防止昂贵且耗时的调试会议。
-
更好的协作:对其他团队成员进行经过良好测试的代码库更容易理解和修改,促进协作并减少引入新错误的可能性。
-
增强的可维护性:全面的测试套件可作为安全网,确保对代码库的未来变化不会无意中打破现有功能。
所以一切都很好。但是,在世界上,我们如何将其应用于CDK并在AWS中建立基础设施?
真实的情况
因此,作为现实世界的情况,我想创建一个简单的安全简单队列服务(SQS)队列。遵循AWS的最佳实践。因此,我们要为SQS队列创建的准则是:
- 队列必须使用KMS加密
- 队列必须有一个死的字母队列才能充当无法处理的消息的溢出。
以后,我们可以添加一个lambda功能,该功能将读取队列并将消息存储在例如S3。
中去构建
现在有趣的东西开始了。让我们从初始化一个新的CDK项目开始。
➜ Hashnode mkdir secure_sqs
➜ Hashnode cd secure_sqs
➜ secure_sqs cdk init app --language=python
Applying project template app for python
# Welcome to your CDK Python project!
This is a blank project for CDK development with Python.
<...SNIPPIT...>
✅ All done!
➜ secure_sqs git:(main) source .venv/bin/activate
(.venv) ➜ secure_sqs git:(main)
我在上述代码块中所做的是在目录secure_sqs中创建一个空的新CDK项目。
查看CDK目录结构,您会发现有一个名为“测试”的目录。这是我们将添加第一个测试的地方。
写一个失败的测试(红色)
我们将使用CDK随附的断言库。有关此的更多信息,请参见此处的AWS CDK文档。
在您喜欢的代码编辑器中打开文件tests/ute/test_secure_sqs_stack.py。该文件已经是带有以下代码的样板(见下文)。我已经删除了最后一个块检查是否创建了SQS队列:
import aws_cdk as core
import aws_cdk.assertions as assertions
from secure_sqs.secure_sqs_stack import SecureSqsStack
# example tests. To run these tests, uncomment this file along with the example resource in secure_sqs/secure_sqs_stack.py
def test_sqs_queue_created():
app = core.App()
stack = SecureSqsStack(app, "secure-sqs")
template = assertions.Template.from_stack(stack)
template.has_resource_properties("AWS::SQS::Queue", {
"VisibilityTimeout": 300
})
如果运行Python测试,此处会发生什么,该测试试图检查合成的云形式模板是否包含AWS :: SQS :: Queue的属性,并且排队的可见度超时为300秒。由于我们没有任何代码创建的代码,因此pytest应该失败。让我们测试一下:
(.venv) ➜ secure_sqs git:(main) pytest <aws:abn>
============================================================= test session starts =============================================================
platform darwin -- Python 3.9.16, pytest-7.2.0, pluggy-1.0.0
rootdir: /Users/yvthepief/Code/Hashnode/secure_sqs
plugins: black-0.3.12, typeguard-2.13.3, cov-4.0.0, syrupy-3.0.5
collected 1 item
tests/unit/test_secure_sqs_stack.py F [100%]
================================================================== FAILURES ===================================================================
___________________________________________________________ test_sqs_queue_created ____________________________________________________________
jsii.errors.JavaScriptError:
@jsii/kernel.RuntimeError: Error: Template has 0 resources with type AWS::SQS::Queue.
No matches found
<...SNIPPIT...>
=========================================================== short test summary info ===========================================================
FAILED tests/unit/test_secure_sqs_stack.py::test_sqs_queue_created - RuntimeError: Error: Template has 0 resources with type AWS::SQS::Queue.
============================================================== 1 failed in 3.25s ==============================================================
您可以看到测试失败。由于该模板具有0个带有AWS类型的资源:: SQS ::队列。这是正确的,因为我们还没有编写任何一行代码。在现实世界的场景部分中,我们描述了我们希望将队列加密,并包含一个死字队列。因此,为此创建测试。
KMS加密队列
由于CDK使用的断言库正在寻找与CloudFormation模板的匹配项,因此可以通过查看AWS :: SQS :: queue资源的官方云形式文档来进行方便的方法。在这里,您可以看到将KMS加密添加到SQS队列中,您需要具有KMSMasterKeyId。
因此,从创建一个测试开始,该测试检查队列是否包含kmsmasterkeyid。由于我们不知道密钥的值,我们可以使用“断言库”中的匹配类。
from aws_cdk.assertions import Match
def test_sqs_queue_is_encrypted():
app = core.App()
stack = SecureSqsStack(app, "secure-sqs")
template = assertions.Template.from_stack(stack)
template.has_resource_properties(
"AWS::SQS::Queue", {"KmsMasterKeyId": Match.any_value()}
)
上面的代码块检查是否在云形式模板中,资源是类型为“ AWS :: SQS :: queue”,并且该资源是用“ KMSMasterKeyId”创建的。
让我们运行pytest,这将失败,当然,顺便说一句,我添加了-v for verbose,-tb = no以禁用追溯:
(.venv) ➜ secure_sqs git:(main) ✗ pytest -v --tb=no <aws:abn>
============================================================= test session starts =============================================================
platform darwin -- Python 3.9.16, pytest-7.2.0, pluggy-1.0.0 -- /opt/homebrew/opt/python@3.9/bin/python3.9
cachedir: .pytest_cache
rootdir: /Users/yvthepief/Code/Hashnode/secure_sqs
plugins: black-0.3.12, typeguard-2.13.3, cov-4.0.0, syrupy-3.0.5
collected 2 items
tests/unit/test_secure_sqs_stack.py::test_sqs_queue_created FAILED [ 50%]
tests/unit/test_secure_sqs_stack.py::test_sqs_queue_is_encrypted FAILED [100%]
----------------------------------------------------------- snapshot report summary -----------------------------------------------------------
=========================================================== short test summary info ===========================================================
FAILED tests/unit/test_secure_sqs_stack.py::test_sqs_queue_created - RuntimeError: Error: Template has 0 resources with type AWS::SQS::Queue.
FAILED tests/unit/test_secure_sqs_stack.py::test_sqs_queue_is_encrypted - RuntimeError: Error: Template has 0 resources with type AWS::SQS::Queue.
============================================================== 2 failed in 3.22s ==============================================================
现在,为无量表添加1个测试。
排队的死信队列
在SQS的上下文中,一个死字母队列是一个队列,用于存储一定数量的重试后主要队列无法处理的消息。这些消息通常是失败的消息,由于问题格式不正确或处理代码中的异常,因此无法处理,F.E.。 Lambda。通过使用DLQ,您可以隔离有问题的消息并研究问题的根本原因。这可以帮助您识别和解决应用程序中的问题,并防止将来发生相同的错误。另外,它为您提供了再次重新处理失败消息的选项。
查看“ aws :: sqs ::队列”的云形式文档,我们可以看到,您需要给您带上一个redrivepolicy,带有一个deadlettertargetarn,指着排队,该排队充当死信队的排队。因此,创建测试:
def test_sqs_queue_has_dead_letter_queue():
app = core.App()
stack = SecureSqsStack(app, "secure-sqs")
template = assertions.Template.from_stack(stack)
template.has_resource_properties(
"AWS::SQS::Queue", {"RedrivePolicy": {"deadLetterTargetArn": Match.any_value()}}
)
再次使用匹配任何值语句,因为我们目前不知道死字母队列。
运行pytest现在应失败3个测试:
FAILED tests/unit/test_secure_sqs_stack.py::test_sqs_queue_created - RuntimeError: Error: Template has 0 resources with type AWS::SQS::Queue.
FAILED tests/unit/test_secure_sqs_stack.py::test_sqs_queue_is_encrypted - RuntimeError: Error: Template has 0 resources with type AWS::SQS::Queue.
FAILED tests/unit/test_secure_sqs_stack.py::test_sqs_queue_has_dead_letter_queue - RuntimeError: Error: Template has 0 resources with type AWS::SQS::Queue.
编写代码以通过测试(绿色)
用CDK创建SQS队列
完成基本测试后,我们现在需要编写实际的测试以通过测试。因此,创建一个带有死字母队列的加密SQS队列。
打开文件secure_sqs/secure_sqs_stack.py。该文件也有一个样板,但我们将进行调整。
from aws_cdk import (
Duration,
Stack,
aws_kms,
aws_sqs,
)
from constructs import Construct
class SecureSqsStack(Stack):
def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
super().__init__(scope, construct_id, **kwargs)
# Create key with rotation and alias
key = aws_kms.Key(
self,
"SecureQueueKmsKey",
alias="/kms/secure_queue_key",
enable_key_rotation=True,
description="Key for encrypting SQS queue",
)
# Create secure encrypted queue with
# visibility timeout of 300 seconds
queue = aws_sqs.Queue(
self,
"SecureQueue",
queue_name="secure_queue",
encryption=aws_sqs.QueueEncryption.KMS,
encryption_master_key=key,
enforce_ssl=True,
visibility_timeout=Duration.seconds(300),
)
查看上面的代码,我们创建一个自定义托管的KMS密钥,该密钥将启用别名和键旋转。该键将用于加密SQS队列。我们还设定了队列策略,以仅允许安全运输(SSL)。最后,我们为可见性超时设置了300秒,如先前创建的测试中指定的。
。因此,在创建队列时,让我们看看测试的运行方式:
(.venv) ➜ secure_sqs git:(main) ✗ pytest -v --tb=no
================================================================== test session starts ==================================================================
platform darwin -- Python 3.11.3, pytest-6.2.5, py-1.11.0, pluggy-1.0.0 -- /Users/yvthepief/Code/Hashnode/secure_sqs/.venv/bin/python3.11
cachedir: .pytest_cache
rootdir: /Users/yvthepief/Code/Hashnode/secure_sqs
plugins: typeguard-2.13.3
collected 3 items
tests/unit/test_secure_sqs_stack.py::test_sqs_queue_created PASSED [ 33%]
tests/unit/test_secure_sqs_stack.py::test_sqs_queue_is_encrypted PASSED [ 66%]
tests/unit/test_secure_sqs_stack.py::test_sqs_queue_has_dead_letter_queue FAILED [100%]
================================================================ short test summary info ================================================================
FAILED tests/unit/test_secure_sqs_stack.py::test_sqs_queue_has_dead_letter_queue - RuntimeError: Error: Template has 1 resources with type AWS::SQS::Q...
============================================================== 1 failed, 2 passed in 5.01s ==============================================================
2已经过去了。但这仍然不是我们目标的100%。要使test_sqs_queue_has_dead_letter_queue也通过,我们需要添加一个死字母队列。在钥匙和队列之间添加死字母队列,并参考排队的死字母队列资源:
from aws_cdk import (
Duration,
Stack,
aws_kms,
aws_sqs,
)
from constructs import Construct
class SecureSqsStack(Stack):
def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
super().__init__(scope, construct_id, **kwargs)
# Create key with rotation and alias
key = aws_kms.Key(
self,
"SecureQueueKmsKey",
alias="/kms/secure_queue_key",
enable_key_rotation=True,
description="Key for encrypting SQS queue",
)
# Create secure encrypted dead letter queue with
# visibility timeout of 300 seconds
dead_letter_queue = aws_sqs.Queue(
self,
"SecureDeadLetterQueue",
queue_name="secure_dead_letter_queue",
encryption=aws_sqs.QueueEncryption.KMS,
encryption_master_key=key,
enforce_ssl=True,
visibility_timeout=Duration.seconds(300),
)
# Create secure encrypted queue with
# visibility timeout of 300 seconds and refer to the dlq
queue = aws_sqs.Queue(
self,
"SecureQueue",
queue_name="secure_queue",
encryption=aws_sqs.QueueEncryption.KMS,
encryption_master_key=key,
enforce_ssl=True,
visibility_timeout=Duration.seconds(300),
dead_letter_queue=aws_sqs.DeadLetterQueue(
max_receive_count=5, queue=dead_letter_queue
),
)
现在测试可以通过吗?
(.venv) ➜ secure_sqs git:(main) ✗ pytest -v --tb=no
================================================================== test session starts ==================================================================
platform darwin -- Python 3.11.3, pytest-6.2.5, py-1.11.0, pluggy-1.0.0 -- /Users/yvthepief/Code/Hashnode/secure_sqs/.venv/bin/python3.11
cachedir: .pytest_cache
rootdir: /Users/yvthepief/Code/Hashnode/secure_sqs
plugins: typeguard-2.13.3
collected 3 items
tests/unit/test_secure_sqs_stack.py::test_sqs_queue_created PASSED [ 33%]
tests/unit/test_secure_sqs_stack.py::test_sqs_queue_is_encrypted PASSED [ 66%]
tests/unit/test_secure_sqs_stack.py::test_sqs_queue_has_dead_letter_queue PASSED [100%]
=================================================================== 3 passed in 4.98s ===================================================================
(.venv) ➜ secure_sqs git:(main) ✗
可怕,它通过了!现在我们可以进一步建立。
您在示例中看到的,我只创建了3个测试,但是查看云信息输出,您可以看到4个AWS资源。要保持合规并测试了所有资源,也是明智的添加这些资源的测试。
额外的选项将是例如,为lambda创建测试,该测试将在安全队列中处理消息,或为SQS队列策略添加测试。但这全部取决于您!现在去构建!
概括
在这篇文章中,我展示了使用CDK使用测试驱动的开发的过程应如何工作。启动小,然后逐步开始。为您将在最终目标应用程序中创建的所有资源创建测试是不明智的。这只会使您的测试混乱。因此,最重要的是您从测试开始,并再次使这些测试保持较小。迭代是这里最重要的因素!