用分页测试boto3 / botocore

时间:2017-11-13 11:32:56

标签: python amazon-web-services boto3

我一直在尝试将单元测试添加到我的AWS脚本中。我一直在使用botocore.stub来存根API调用。

我需要为各种调用添加分页,我似乎无法找到一种方法来编写包含分页的测试。

以下是非分页测试的示例,我想知道如何重构此测试和函数以使用分页:

 # -*- coding: utf-8 -*-
 import unittest
 import boto3
 from botocore.stub import Stubber
 from datetime import datetime


 def describe_images(client, repository):
     return client.describe_images(repositoryName=repository)


 class TestCase(unittest.TestCase):
     def setUp(self):
         self.client = boto3.client('ecr')

     def test_describe_images(self):
         describe_images_response = {
             'imageDetails': [
                 {
                     'registryId': 'string',
                     'repositoryName': 'string',
                     'imageDigest': 'string',
                     'imageTags': [
                         'string',
                     ],
                     'imageSizeInBytes': 123,
                     'imagePushedAt': datetime(2015, 1, 1)
                 },
             ],
             'nextToken': 'string'
         }
         stubber = Stubber(self.client)
         expected_params = {'repositoryName': 'repo_name'}
         stubber.add_response(
             'describe_images',
             describe_images_response,
             expected_params
         )
         with stubber:
             response = describe_images(self.client, 'repo_name')

         self.assertEqual(describe_images_response, response)


 if __name__ == '__main__':
     unittest.main()

如果我更新函数以包含这样的分页:

 def describe_images(client, repository):
     paginator = client.get_paginator('describe_images')

     response_iterator = paginator.paginate(
         repositoryName=repository
     )
     return response_iterator
我们似乎正在某个地方。测试失败,因为它应该在平等发生变化时失败:

F
======================================================================
FAIL: test_describe_images (__main__.TestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "desc_imgs_paginated.py", line 47, in test_describe_images
    self.assertEqual(describe_images_response, response)
AssertionError: {'imageDetails': [{'registryId': 'string'[178 chars]ing'} != <botocore.paginate.PageIterator object at 0x1058649b0>

----------------------------------------------------------------------
Ran 1 test in 0.075s

FAILED (failures=1)

当我尝试迭代生成器::

def describe_images(client, repository):
     paginator = client.get_paginator('describe_images')

     response_iterator = paginator.paginate(
         repositoryName=repository
     )
     return [r for r in response_iterator]

我收到以下错误:

 E
 ======================================================================
 ERROR: test_describe_images (__main__.TestCase)
 ----------------------------------------------------------------------
 Traceback (most recent call last):
   File "desc_imgs_paginated.py", line 45, in test_describe_images
     response = describe_images(self.client, repo_name)
   File "desc_imgs_paginated.py", line 14, in describe_images
     return '.join([r for r in response_iterator])
   File "desc_imgs_paginated.py", line 14, in <listcomp>
     return '.join([r for r in response_iterator])
   File "lib/python3.6/site-packages/botocore/paginate.py", line 255, in __iter__
     response = self._make_request(current_kwargs)
   File "lib/python3.6/site-packages/botocore/paginate.py", line 332, in _make_request
     return self._method(**current_kwargs)
   File "lib/python3.6/site-packages/botocore/client.py", line 312, in _api_call
     return self._make_api_call(operation_name, kwargs)
   File "lib/python3.6/site-packages/botocore/client.py", line 579, in _make_api_call
     api_params, operation_model, context=request_context)
   File "lib/python3.6/site-packages/botocore/client.py", line 631, in _convert_to_request_dict
     params=api_params, model=operation_model, context=context)
   File "lib/python3.6/site-packages/botocore/hooks.py", line 227, in emit
     return self._emit(event_name, kwargs)
   File "lib/python3.6/site-packages/botocore/hooks.py", line 210, in _emit
     response = handler(**kwargs)
   File "lib/python3.6/site-packages/botocore/stub.py", line 337, in _assert_expected_params
     self._assert_expected_call_order(model, params)
   File "lib/python3.6/site-packages/botocore/stub.py", line 323, in _assert_expected_call_order
     pformat(params)))
 botocore.exceptions.StubResponseError: Error getting response stub for operation DescribeImages: Unexpected API Call: called with parameters:
 {nextToken: string, repositoryName: repo_name}

 ----------------------------------------------------------------------
 Ran 1 test in 0.051s

 FAILED (errors=1)

我错过了正确的测试方法吗?或者这是boto3 / botocore中的错误?

1 个答案:

答案 0 :(得分:0)

问这个问题已经有一段时间了,但因为没有答案..

在您的设置中,您提供如下响应字典

describe_images_response = {
    'imageDetails': [
        {
            'registryId': 'string',
            'repositoryName': 'string',
            'imageDigest': 'string',
            'imageTags': [
                'string',
            ],
            'imageSizeInBytes': 123,
            'imagePushedAt': datetime(2015, 1, 1)
        },
    ],
    'nextToken': 'string'
}

这里的关键是第一个响应将包含一个 nextToken 值。这将导致来自分页器的第二个请求。因此,您必须为存根提供额外的响应,最终您需要以包含 nextToken

的响应结束

现在回头看看你的设置,只有一个 add_response 调用 stubber

stubber.add_response(
    'describe_images',
    describe_images_response,
    expected_params
)

最终结果是当分页器发出第二个请求时,设置中没有指定响应。

这导致异常,希望现在更有意义的消息

 botocore.exceptions.StubResponseError: Error getting response stub for operation DescribeImages: Unexpected API Call: called with parameters:
 {nextToken: string, repositoryName: repo_name}

由于尚未设置第二个响应,因此您会收到意外请求的异常,在此请求中您可以看到 nextToken 参数的规范。