简体   繁体   English

模拟 dynamodb 表 update_item 调用的 boto3 (botocore) ClientError

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM