boto / botocore

The low-level, core functionality of boto3 and the AWS CLI.
Apache License 2.0
1.46k stars 1.07k forks source link

Stubbing custom commands #974

Open icyc9 opened 8 years ago

icyc9 commented 8 years ago

When calling stubber.add_response with certain boto3 s3 client methods I have encountered "botocore.model.OperationNotFoundError".

import unittest

import boto3
from botocore.stub import Stubber

def setup_s3_client():
    return boto3.client('s3')

MOCK_BUCKET_NAME = 'test-bucket'

create_bucket_response = {
    'Location': '/' + MOCK_BUCKET_NAME,
    'ResponseMetadata': {
        'HTTPStatusCode': 200,
        'HostId': 'myHostId',
        'RequestId': '80678CD98525D782'
    }
}

upload_file_response = {
    'ServerSideEncryption': 'AES256',
    'ETag': 'string',
    'SSECustomerAlgorithm': 'string',
    'SSECustomerKeyMD5': 'string',
    'SSEKMSKeyId': 'string',
    'RequestCharged': 'requester'
}

class TestS3Bucket(unittest.TestCase):

    def setUp(self):
        self.client = setup_s3_client()
        self.stubber = Stubber(self.client)
        self.stubber.activate()

    def test_create_bucket(self):
        self.stubber.add_response(
            method='create_bucket',
            service_response=create_bucket_response,
            expected_params={'Bucket': MOCK_BUCKET_NAME})

        self.client.create_bucket(Bucket=MOCK_BUCKET_NAME)

    def test_upload(self):
        self.stubber.add_response(
            method='upload_file',
            service_response=upload_file_response,
            expected_params={'Bucket': MOCK_BUCKET_NAME, 'Filename': 'fake_source_uri', 'Key': 'test_key'})

        self.client.upload_file(Filename='fake_filename',
                                Bucket=MOCK_BUCKET_NAME,
                                Key='fake_key')

The test results are shown as follows.

       @instance_cache
    def operation_model(self, operation_name):
        try:
            model = self._service_description['operations'][operation_name]
        except KeyError:
>           raise OperationNotFoundError(operation_name)
E           botocore.model.OperationNotFoundError

env_name/lib/python3.5/site-packages/botocore/model.py:249: OperationNotFoundError
=========================================================================== 1 failed, 1 passed in 0.29 seconds ============================================================================

The add_response method does not seem to allow the 'upload_file' client method to be stubbed. I have also found this to be true with the 'download_file' client method.

JordonPhillips commented 8 years ago

That's because download_file and upload_file are customizations which live in boto3. They call out to one or many requests under the hood. Right now there's not a great story for supporting customizations other than recording underlying commands they use and adding them to the stubber. There's an external library that can handle that for you, though we don't support it ourselves.

jnoring commented 8 years ago

Just hit this too. It'd be really great if stubber could handle download_file and upload_file

montaro commented 7 years ago

@JordonPhillips Thanks for your reply. What's the external library that mocks the upload_file()?

JordonPhillips commented 7 years ago

placebo, though it runs in a different way than the stubber does: it records and replays.

luck02 commented 7 years ago

I just found out that 'get_paginator' seem to be the same story. It is a little confusing for me at this point as it seemed the stubber was for boto3, the relationship between botocore and boto3 is a bit unclear to me. I'll check out placebo.

crizzy9 commented 3 years ago

Anyone found a workaround for this yet?

bosatsu commented 3 years ago

In case anyone else comes across, this I found that for get_paginator, rather than adding a response with get_paginator as the service method, instead you should add a response for the service method that get_paginator would access. For example

Instead of:

cfn_stubber = Stubber(cloudformation_client)
cfn_stubber.add_response('get_paginator', expected_response, 'describe_stacks')

Do:

cfn_stubber = Stubber(cloudformation_client)
cfn_stubber.add_response('describe_stacks', expected_response)

The stubber does seem able to return a paginator object that includes the data passed from the expected_response.

mhihasan commented 2 years ago

Any update on how to mock upload_file using stubber?

robertsonwang commented 1 year ago

Any update on how to mock upload_file using stubber?

Use put_object instead of upload_file