【问题标题】:How to properly patch boto3 calls in unit test如何在单元测试中正确修补 boto3 调用
【发布时间】:2019-07-16 21:31:31
【问题描述】:

我是 Python 单元测试的新手,我想模拟对 boto3 3rd 方库的调用。这是我的精简代码:

real_code.py:

import boto3

def temp_get_variable(var_name):
  return boto3.client('ssm').get_parameter(Name=var_name)['Parameter']['Value']

test_real_code.py:

import unittest
from datetime import datetime
from unittest.mock import patch

import real_code

class TestRealCode(unittest.TestCase):

    @patch('patching_config.boto3.client')
    def test_get_variable(self, mock_boto_client):

        response = {
            'Parameter': {
                'Name': 'MyTestParameterName',
                'Type': 'String',
                'Value': 'myValue',
                'Version': 123,
                'Selector': 'asdf',
                'SourceResult': 'asdf',
                'LastModifiedDate': datetime(2019, 7, 16),
                'ARN': 'asdf'
            }
        }

        mock_boto_client.get_variable.return_value = response

        result_value = real_code.get_variable("MyTestParameterName")

        self.assertEqual("myValue", result_value)

当我运行它时,测试失败了

Expected :myValue
Actual   :<MagicMock name='client().get_parameter().__getitem__().__getitem__()' id='2040071816528'>

我做错了什么?我认为通过设置mock_boto_client.get_variable.return_value = response 它会模拟呼叫并返回我的预设响应。我不明白为什么我得到一个 MagicMock 对象而不是我试图设置的返回值。我想设置我的测试,以便当使用特定参数调用get_parameter 时,模拟返回我在测试中指定的预设响应。

【问题讨论】:

标签: python unit-testing mocking boto3


【解决方案1】:

您的测试代码存在两个问题。第一个是当你的模拟对象mock_boto_client 被调用时,它会返回一个新的模拟对象。这意味着正在调用get_parameter() 的对象与您尝试设置返回值的对象不同。您可以通过以下方式让它自行返回:

mock_boto_client.return_value = mock_boto_client

您还可以使用不同的模拟对象:

foo = MagicMock()
mock_boto_client.return_value = foo

您遇到的第二个问题是您在模拟错误的方法调用。 mock_boto_client.get_variable.return_value 应该是 mock_boto_client.get_parameter.return_value。这是更新和工作的测试:

import unittest
from datetime import datetime
from unittest.mock import patch

import real_code

class TestRealCode(unittest.TestCase):

    @patch('boto3.client')
    def test_get_variable(self, mock_boto_client):

        response = {
            'Parameter': {
                'Name': 'MyTestParameterName',
                'Type': 'String',
                'Value': 'myValue',
                'Version': 123,
                'Selector': 'asdf',
                'SourceResult': 'asdf',
                'LastModifiedDate': datetime(2019, 7, 16),
                'ARN': 'asdf'
            }
        }

        mock_boto_client.return_value = mock_boto_client
        mock_boto_client.get_parameter.return_value = response

        result_value = real_code.get_variable("MyTestParameterName")

        self.assertEqual("myValue", result_value)

【讨论】:

  • 我想我理解第一个问题的解释。所以@patch 行本质上只是用 mock_boto_client 变量引用的 MagicMock 对象替换了 boto3 对象的“client”成员......这意味着我仍然需要设置调用该 MagicMock 对象时会发生什么。这是正确的理解吗?
  • @Shawn Close - 它正在替换 boto3 moduleclient function。默认情况下,mock 为所有未明确设置的返回值创建一个新的 mock 对象。
猜你喜欢
  • 1970-01-01
  • 2020-01-03
  • 2021-10-31
  • 1970-01-01
  • 2012-09-01
  • 2013-08-15
  • 1970-01-01
  • 1970-01-01
  • 2017-11-17
相关资源
最近更新 更多