[英]Mock a boto3 (botocore) ClientError for a dynamodb Table update_item call
I am writing a unit test for a python function that does a update_item in dynamodb via the table resource.我正在为 python function 编写一个单元测试,它通过表资源在 dynamodb 中执行 update_item。 The normal unit test works but raising a ClientError is something I cannot ge to work..正常的单元测试有效,但引发 ClientError 是我无法工作的事情。
The tests get executed but I alwys: E Failed: DID NOT RAISE <class 'botocore.exceptions.ClientError'>
测试得到执行,但我总是: E Failed: DID NOT RAISE <class 'botocore.exceptions.ClientError'>
What I am trying to do is simulate the case when I would get a Internal Server Error of AWS or a RequestLimitExceeded error.我想要做的是模拟出现 AWS 内部服务器错误或 RequestLimitExceeded 错误的情况。
Thanks for pointing me in the correct direction!感谢您为我指明正确的方向!
My functional code:我的功能代码:
def _updateCardConfiguration(identifier, cardType, layout):
try:
table.update_item(
Key={
'identifier': identifier,
'cardType': cardType
},
UpdateExpression="SET layout = :layout",
ExpressionAttributeValues={
':layout': layout
}
)
except ClientError as e:
logger.fatal(
f"The update of the cardconfiguration for the niche: {identifier} has failed with the following message: {e.response['Error']['Message']}")
raise
return True
def lambda_handler(event, context):
# Check the cardtype
cardType = 'SearchresultCard' if (
event['detail']['action'] == 'updateSearchresultCard') else 'DetailCard'
# Update the card configuration
_updateCardConfiguration(
event['detail']['identifier'], cardType, event['detail']["layout"])
return True
My test code:我的测试代码:
@pytest.fixture(scope='function')
def cardConfigurationsTable(aws_credentials):
with mock_dynamodb2():
#client = boto3.client('dynamodb', region_name='eu-west-1')
dynamodb = boto3.resource('dynamodb')
dynamodb.create_table(
TableName='CardConfigurations',
KeySchema=[
{
'AttributeName': 'identifier',
'KeyType': 'HASH'
},
{
'AttributeName': 'cardType',
'KeyType': 'RANGE'
}
],
AttributeDefinitions=[
{
'AttributeName': 'identifier',
'AttributeType': 'S'
},
{
'AttributeName': 'cardType',
'AttributeType': 'S'
},
],
StreamSpecification={
'StreamEnabled': True,
'StreamViewType': 'NEW_AND_OLD_IMAGES'
},
BillingMode='PAY_PER_REQUEST',
SSESpecification={
'Enabled': True
},
GlobalSecondaryIndexes=[
{
'IndexName': 'byIdentifier',
'KeySchema': [
{
'AttributeName': 'identifier',
'KeyType': 'HASH'
},
{
'AttributeName': 'cardType',
'KeyType': 'RANGE'
}
],
'Projection': {
'ProjectionType': 'ALL'
}
}
])
yield dynamodb.Table('CardConfigurations')
def test_updateItem_raises_clientExecption(cardConfigurationsTable, env_vars):
""" Unit test for testing the lambda that updates the cardconfiguration
Scenario: No detail card is configured for the niche
:param fixture cardConfigurationsTable: The fixture that mocks the dynamodb CardConfigurations table
:param fixture env_vars: The fixture with the env vars
"""
# Prepare the event
with open('tests/unit/event-flows/dataconfiguration-state-transition/events/updateSearchresultCard.json') as json_file:
event = json.load(json_file)
stubber = Stubber(cardConfigurationsTable.meta.client)
stubber.add_client_error(
"update_item",
service_error_code="InternalServerError",
service_message="Internal Server Error",
http_status_code=500,
)
stubber.activate()
# Import the lambda handler
lambdaFunction = importlib.import_module(
"event-flows.dataconfiguration-state-transition.updateCardconfigurationInDb.handler")
with pytest.raises(ClientError) as e_info:
# Execute the handler
response = lambdaFunction.lambda_handler(event, {})
I would start by creating a default table that your _updateCardConfiguration
function can use.我将首先创建一个您的_updateCardConfiguration
function 可以使用的默认表。
def get_table():
table = dynamodb.Table("CardConfigurations")
return table
default_table = get_table()
You can then pass it as a default argument to the function that needs it.然后,您可以将它作为默认参数传递给需要它的 function。 This makes it easier to test the function.这样可以更轻松地测试 function。 To do so, modify your _updateCardConfiguration
function header to look like this:为此,请将您的_updateCardConfiguration
function header 修改为如下所示:
def _updateCardConfiguration(identifier, cardType, layout, table=default_table):
Doing this gives us the ability to replace the default table with a mocked table for testing.这样做使我们能够用模拟表替换默认表以进行测试。 The complete modified function looks like this:完整修改后的 function 如下所示:
def _updateCardConfiguration(identifier, cardType, layout, table=default_table):
try:
table.update_item(
Key={"identifier": identifier, "cardType": cardType},
UpdateExpression="SET layout = :layout",
ExpressionAttributeValues={":layout": layout},
)
except ClientError as e:
logger.fatal(
f"The update of the cardconfiguration for the niche: {identifier} has failed with the following message: {e.response['Error']['Message']}"
)
raise
return True
As you can see, all that changes is the header.如您所见,所有的变化都是 header。
Then in your test_updateItem_raises_clientExecption
test function file, I would change the lines然后在您的test_updateItem_raises_clientExecption
测试 function 文件中,我会更改行
with pytest.raises(ClientError) as e_info:
# Execute the handler
response = lambdaFunction.lambda_handler(event, {})
to:至:
with pytest.raises(ClientError, match="InternalServerError"):
# Execute the _updateCardConfiguration function
lambdaFunction._updateCardConfiguration(
None, None, None, cardConfigurationsTable
)
By passing in your cardConfigurationsTable
fixture, your function wiil operate against it and you should get the behaviour you expect.通过传入您的cardConfigurationsTable
夹具,您的 function 将对它进行操作,您应该得到您期望的行为。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.