【问题标题】:Using Mock in Python for nested objects (DynamoDB and Table)在 Python 中为嵌套对象(DynamoDB 和表)使用 Mock
【发布时间】:2018-08-13 21:13:05
【问题描述】:

我想使用来自 DynamoDb 的不同输入来测试函数的行为。有两种主要行为:在表中找到搜索键时和未在表中找到时。这是函数的最小代码:

import boto3
from boto3.dynamodb.conditions import Key

def main(symbol):
   dynamo = boto3.resource("dynamodb")
   table = dynamo.Table("mytable")
   data = table.query(KeyConditionExpression=Key("symbol").eq(symbol))

   if data.count > 0:
      # result = some_output
   else:
      result = {'status': '404'}
   return result

我想通过单元测试通过发送空结果和非空项目列表来测试此代码,类似于以下内容:

import boto3
import unittest
from unittest.mock import Mock, patch

class TestMainHandler(unittest.TestCase): 
   ...
   def test_main_fails_on_wrong_symbol(self):
       with patch.object(main_handler, 'table') as get_mock:
          get_mock.return_value = []
          result = main('dummy_symbol')
          expect_result = {'status': '404'}
   self.assertEqual(result, expect_result)

但我无法运行模拟部分。我想知道您是否可以指导我如何模拟嵌套表和发电机变量。非常感谢您的帮助。

【问题讨论】:

    标签: python mocking python-unittest


    【解决方案1】:

    我建议尽可能模拟第一个“集成点”,然后以此构建模拟结果。在这种情况下,它将是boto3.resource。从那里,您可以将boto3.resource 的返回值修改为模拟表。然后,您可以将模拟表上任何调用的返回值更改为您的预期结果。

    import boto3
    import unittest
    from unittest.mock import Mock, patch
    
    class TestMainHandler(unittest.TestCase): 
        @patch('boto3.resource')
        def test_main_fails_on_wrong_symbol(self, mock_dynamo):
            mock_table = Mock()
            mock_table.query.return_value = []
            mock_dynamo.Table.return_value = mock_table
    
            result = main('dummy_symbol')
            expected_result = {'status': '404'}
            self.assertEqual(expected_result, result)
    

    另外请注意,我在测试用例上使用了补丁装饰器,而不是上下文管理器。这是我的偏好问题,但我认为它看起来更干净。

    编辑:为mock_dynamo 函数调用分配返回值时出现错误。我还填写了你的if 来测试查询数据的长度并返回一些东西。不过,这里的测试没有到达那个分支。这是最终产品:

    """boto_main.py"""
    import boto3
    from boto3.dynamodb.conditions import Key
    
    def main(symbol):
       dynamo = boto3.resource("dynamodb")
       table = dynamo.Table("mytable")
       data = table.query(KeyConditionExpression=Key("symbol").eq(symbol))
       if len(data) > 0:
          result = {'status': '200'}
       else:
          result = {'status': '404'}
       return result
    

    """boto_test.py"""
    import unittest
    from unittest.mock import Mock, patch
    from boto_main import main
    
    class TestMainHandler(unittest.TestCase): 
        @patch('boto3.resource')
        def test_main_fails_on_wrong_symbol(self, mock_dynamo):
            mock_table = Mock()
            mock_table.query.return_value = []
            mock_dynamo.return_value.Table.return_value = mock_table
            result = main('dummy_symbol')
            expected_result = {'status': '404'}
            self.assertEqual(expected_result, result)
    

    【讨论】:

    • 非常好的主意@wholevinski。我会按照你的建议实施。感谢您的帮助!
    • 我完全按照您的建议编写了代码,但似乎没有应用路径。这是我收到的错误:botocore.errorfactory.ResourceNotFoundException: An error occurred (ResourceNotFoundException) when calling the Query operation: Requested resource not found。谢谢
    • 我现在无法对其进行测试,但要对其进行调试,您可能需要在您正在测试的函数中放置 pdb.set_trace() 并查看没有被模拟的内容。我按照书面形式对其进行了测试,它对我有用。
    • 不客气。几天后我才能再次尝试,但我会看看我是否可以进一步调试。
    • 是的,所以这一行:@patch('boto3.resource') 模拟了函数。所以我本质上是将Table.return_value ... 分配给Mock 实例上的一个属性(这是错误的)。缺少的.return_value 使得每当调用该模拟函数时,都会返回预期的返回值。
    猜你喜欢
    • 2019-09-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-24
    • 2013-01-20
    • 1970-01-01
    • 2018-04-27
    • 1970-01-01
    相关资源
    最近更新 更多