首页 > 解决方案 > 由于moto的模拟,我如何在另一个不一定存在的函数中模拟对象的函数调用

问题描述

我有一个名为 check_stuff 的函数,它实例化一个对象并调用该函数describe_continuous_backups,但是,moto 尚不支持此功能,因此我需要自己手动模拟它。我有以下内容,但似乎我无法修补该对象。我该怎么办?

def check_stuff(profile, table_name):
    session = boto3.Session(profile_name=profile)
    client = session.client('dynamodb', 'eu-west-1')
    some_stuff = client.describe_continuous_backups(TableName=table_name)
    #dostuff

@mock_dynamodb2
@mock.patch('boto3.Session.client.describe_continuous_backups', return_value={'foo': 'bar'})
def test_continuous_backup_disabled(self):
    table = self.client.create_table(
        TableName='Movies',
        KeySchema=[
            {
                'AttributeName': 'year',
                'KeyType': 'HASH'
            },
            {
                'AttributeName': 'title',
                'KeyType': 'RANGE'
            }
        ],
        AttributeDefinitions=[
            {
                'AttributeName': 'year',
                'AttributeType': 'N'
            },
            {
                'AttributeName': 'title',
                'AttributeType': 'S'
            },

        ],
        ProvisionedThroughput={
            'ReadCapacityUnits': 10,
            'WriteCapacityUnits': 10
        }
    )
    result = check_stuff('myprofile', 'some_table')

我可以尝试像这样模拟客户:

mocker.patch('mypackage.boto3.Session.client', ....)

但问题在于它模拟了客户端本身。我需要模拟一个不一定存在的功能,同时保留其余功能。

标签: pythontestingmocking

解决方案


boto3.client根据参数返回一个动态创建的类的实例service_name(参见源代码),因此您不能使用该patch方法,该方法要求目标对象是可导入的。

相反,您可以 patch botocore.client.ClientCreator._create_methods,该方法为返回的类动态创建方法boto3.client,并使用包装函数使describe_continuous_backups属性成为Mock具有给定的对象return_value

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

def override(*args, **kwargs):
    def wrapper(self, service_model):
        op_dict = original_create_methods(self, service_model)
        if 'describe_continuous_backups' in op_dict:
            op_dict['describe_continuous_backups'] = Mock(*args, **kwargs)
        return op_dict
    return wrapper
original_create_methods = botocore.client.ClientCreator._create_methods

@patch('botocore.client.ClientCreator._create_methods', override(return_value={'foo': 'bar'}))
def check_stuff():
    session = boto3.Session()
    client = session.client('dynamodb', 'eu-west-1')
    some_stuff = client.describe_continuous_backups(TableName='')
    return some_stuff

print(check_stuff())

这输出:

{'foo': 'bar'}

推荐阅读