簡體   English   中英

模擬AWS lambda和boto3

[英]Mocking AWS lambda and boto3

我有一個lambda(python),它返回客戶檔案的json表示。 它通過從頂級帳戶json開始,然后讀取鏈接的json文件直到它用完鏈接來完成此操作。 從s3讀取的函數是遞歸的,但遞歸只會是一個深度。

這是實際從給定鍵獲取json內容的方法。 (斗已知)

def get_index_from_s3(key):
    try:
        response = s3.get_object(
            Bucket=bucket,
            Key=key
        )
        body = response.get('Body')
        content = body.read().decode('utf-8')
    except ClientError as ex:
        # print 'EXCEPTION MESSAGE: {}'.format(ex.response['Error']['Code'])
        content = '{}'

    message = json.loads(content)
    return message

代碼返回在指定鍵處找到的json,或者在get_object由於ClientError(由NoSuchKey產生的結果)而失敗的情況下返回空字典。

我測試了這個,它的確有效。 對函數的第一次調用獲得了一大塊json。 解析json,找到鏈接,進行第二次調用,然后構建配置文件。 如果我刪除鏈接鍵上的對象,我只是按預期獲得默認的空表示。

我的問題來自測試這個。 我寫了幾個測試類,每個測試類都有一個編排方法,他們共享一個act方法。

為了我的快樂道路,我使用以下安排:

def arrange(self):
    super(WhenCognitoAndNerfFoundTestCase, self).arrange()
    # self.s3_response = self.s3.get_object.return_value
    self.s3_body = self.s3.get_object.return_value.get.return_value
    self.s3_body.read.return_value.decode.side_effect = [
        self.cognito_content,
        self.nerf_content]
    signed_url = "https://this.is/a/signed/url/index.html"
    self.s3.generate_presigned_url.return_value = signed_url

這正是我想要的。 s3_response是get_object的return_value,它具有get返回的Body屬性,后續讀取值返回json字符串。 我使用side_effect設置為json字符串列表,這樣我就可以在每次調用時返回一個不同的字符串(只有兩個) content = body.read().decode('utf-8')

但是當我想測試第二桶中缺少內容的情況時,我受到了阻礙。 我目前對這種安排的嘗試如下:

def arrange(self):
    super(WhenCognitoOnlyFoundTestCase, self).arrange()
    # self.s3_response = MagicMock()
    # botocore.response.StreamingBody
    self.s3.get_object.side_effect = [{},
                                      ClientError]
    # self.s3_response = self.s3.get_object.return_value
    self.s3_body = self.s3.get_object.return_value.get.return_value
    self.s3_body.read.return_value.decode.return_value = \
        self.cognito_content

運行測試會產生以下結果:

    def get_index_from_s3(key):
        try:
            response  = s3.get_object(
                Bucket=bucket,
                Key=key
            )
            body = response.get('Body')
>           content = body.read().decode('utf-8')
E            AttributeError: 'NoneType' object has no attribute 'read'

master_profile.py:66: AttributeError

這是有道理的,因為read方法位於s3.get_object響應的Body屬性上,在此方案中為None。

所以我的問題是,我如何模仿這個東西,以便我可以測試它? 模擬get_object響應的難點在於,盡管它只是一個字典,但Body屬性是一個botocore.response.StreamingBody ,我不知道如何模擬。

根據經驗,您的目標應該是讓您的問題自成一體。 為了說明你做錯的一些事情,我稍微修改了你的初始函數,使它自成一體。

讓我們s3_module我們要測試的s3_module定義如下:

import boto3
from botocore.exceptions import ClientError
import json

s3 = boto3.client('s3')

def get_index_from_s3(key):
    try:
        response = s3.get_object(
            Bucket='bucket',
            Key=key
        )
        body = response.get('Body')
        content = body.read().decode('utf-8')
    except ClientError as ex:
        import ipdb; ipdb.set_trace()
        # print 'EXCEPTION MESSAGE: {}'.format(ex.response['Error']['Code'])
        content = '{}'

    message = json.loads(content)
    return message

為了測試它,我們可以編寫另一個模塊s3_test ,其測試類似於:

import pytest
from unittest.mock import patch, Mock, MagicMock
from botocore.exceptions import ClientError
import json

from s3_module import get_index_from_s3


@patch('s3_module.s3.get_object')
def test_get_index_from_s3(s3_get_mock):

    body_mock = Mock()
    body_mock.read.return_value.decode.return_value = json.dumps('first_response')
    s3_get_mock.side_effect = [{'Body': body_mock}, ClientError(MagicMock(), MagicMock())]

    first_response = get_index_from_s3('key1')
    assert  first_response == 'first_response'
    second_response = get_index_from_s3('key2')
    assert  second_response == {}

與您的解決方案相比,您錯過了一些要點:

  • self.s3.get_object.side_effect應返回第一個響應的對象,該響應與其余代碼一起使用,即包含Body鍵的字典,其內容可以read()decoded()並由json.load()

  • self.s3.get_object.side_effect應返回為第二個響應正確構造的ClientError異常

您可以在botocore文檔中查看有關如何構建ClientError異常的更多信息: http ://botocore.readthedocs.io/en/latest/client_upgrades.html#error-handling

您可以在文檔中找到有關修補和模擬的更多信息: https//docs.python.org/3/library/unittest.mock.html

通常關於補丁的位置部分非常有用: https//docs.python.org/3/library/unittest.mock.html#where-to-patch

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM