티스토리 뷰

반응형

Stack Overflow에 자주 검색, 등록되는 문제들과 제가 개발 중 찾아 본 문제들 중에서 나중에도 찾아 볼 것 같은 문제들을 정리하고 있습니다.

Stack Overflow에서 가장 먼저 확인하게 되는 가장 높은 점수를 받은 Solution과 현 시점에 도움이 될 수 있는 가장 최근에 업데이트(최소 점수 확보)된 Solution을 각각 정리하였습니다.

 

아래 word cloud를 통해 이번 포스팅의 주요 키워드를 미리 확인하세요.

Mocking boto3 S3 client method Python

파이썬에서 boto3 S3 클라이언트 메소드를 모킹하는 방법

 문제 내용 

I'm trying to mock a singluar method from the boto3 s3 client object to throw an exception. But I need all other methods for this class to work as normal.

저는 boto3 S3 클라이언트 객체에서 특정 메소드가 예외를 던지도록 mocking을 시도하고 있습니다. 하지만 이 클래스의 다른 모든 메소드는 정상적으로 작동해야 합니다.

 

This is so I can test a singular Exception test when and error occurs performing a upload_part_copy

이는 upload_part_copy를 수행하는 동안 오류가 발생했을 때 특정 예외 상황을 테스트하기 위한 것입니다.

 

1st Attempt

첫 번째 시도

 

import boto3
from mock import patch

with patch('botocore.client.S3.upload_part_copy', side_effect=Exception('Error Uploading')) as mock:
    client = boto3.client('s3')
    # Should return actual result
    o = client.get_object(Bucket='my-bucket', Key='my-key')
    # Should return mocked exception
    e = client.upload_part_copy()

 

However this gives the following error:

그러나 이렇게 하면 다음과 같은 오류가 발생합니다:
ImportError: No module named S3

 

2nd Attempt

2번째 시도

 

After looking at the botocore.client.py source code I found that it is doing something clever and the method upload_part_copy does not exist. I found that it seems to call BaseClient._make_api_call instead so I tried to mock that

botocore.client.py의 소스 코드를 살펴보니, upload_part_copy 메서드가 존재하지 않고 BaseClient._make_api_call을 호출하는 것으로 보입니다. 따라서 저는 이를 모킹해보려고 시도해 보았습니다.
import boto3
from mock import patch

with patch('botocore.client.BaseClient._make_api_call', side_effect=Exception('Error Uploading')) as mock:
    client = boto3.client('s3')
    # Should return actual result
    o = client.get_object(Bucket='my-bucket', Key='my-key')
    # Should return mocked exception
    e = client.upload_part_copy()

 

This throws an exception... but on the get_object which I want to avoid.

이 방법은 예외를 발생시키지만, 내가 피하고 싶은 get_object에서 예외가 발생합니다.

 

Any ideas about how I can only throw the exception on the upload_part_copy method?

upload_part_copy 메소드에서만 예외를 발생시키도록 할 수 있는 아이디어가 있을까요?

 

 

 

 높은 점수를 받은 Solution 

Botocore has a client stubber you can use for just this purpose: docs.

Botocore에는 이 목적으로 사용할 수 있는 클라이언트 스터버가 있습니다: docs.

 

Here's an example of putting an error in:

여기 에러를 넣는 예시가 있습니다: 
import boto3
from botocore.stub import Stubber

client = boto3.client('s3')
stubber = Stubber(client)
stubber.add_client_error('upload_part_copy')
stubber.activate()

# Will raise a ClientError
client.upload_part_copy()

 

Here's an example of putting a normal response in. Additionally, the stubber can now be used in a context. It's important to note that the stubber will verify, so far as it is able, that your provided response matches what the service will actually return. This isn't perfect, but it will protect you from inserting total nonsense responses.

다음은 정상적인 응답을 넣은 예시입니다. 더불어, 스텁 객체는 이제 컨텍스트에서 사용할 수 있습니다. 중요한 점은 스텁 객체가 제공된 응답이 서비스가 실제로 반환할 것과 일치하는지 검증하므로 완벽하지는 않지만, 완전한 헛소리 응답을 삽입하는 것을 방지합니다.
import boto3
from botocore.stub import Stubber

client = boto3.client('s3')
stubber = Stubber(client)
list_buckets_response = {
    "Owner": {
        "DisplayName": "name",
        "ID": "EXAMPLE123"
    },
    "Buckets": [{
        "CreationDate": "2016-05-25T16:55:48.000Z",
        "Name": "foo"
    }]
}
expected_params = {}
stubber.add_response('list_buckets', list_buckets_response, expected_params)

with stubber:
    response = client.list_buckets()

assert response == list_buckets_response

 

 

 가장 최근 달린 Solution 

Here is my solution for patching a boto client used in the bowels of my project, with pytest fixtures. I'm only using 'mturk' in my project.

제 프로젝트 내부에서 사용하는 boto 클라이언트를 pytest 픽스처로 패치하는 방법입니다. 저는 프로젝트에서 'mturk'만 사용하고 있습니다.

 

The trick for me was to create my own client, and then patch boto3.client with a function that returns that pre-created client.

제 해결책은 내가 직접 만든 클라이언트를 생성하고, 그리고 boto3.client를 패치하는 함수를 만들어 미리 만든 클라이언트를 반환하도록 하는 것이었다.
@pytest.fixture(scope='session')
def patched_boto_client():
    my_client = boto3.client('mturk')

    def my_client_func(*args, **kwargs):
        return my_client

    with patch('bowels.of.project.other_module.boto3.client', my_client_func):
        yield my_client_func


def test_create_hit(patched_boto_client):    
    client = patched_boto_client()
    stubber = Stubber(client)
    stubber.add_response('create_hit_type', {'my_response':'is_great'})
    stubber.add_response('create_hit_with_hit_type', {'my_other_response':'is_greater'})
    stubber.activate()

    import bowels.of.project # this module imports `other_module`
    bowels.of.project.create_hit_function_that_calls_a_function_in_other_module_which_invokes_boto3_dot_client_at_some_point()

 

I also define another fixture that sets up dummy aws creds so that boto doesn't accidentally pick up some other set of credentials on the system. I literally set 'foo' and 'bar' as my creds for testing -- that's not a redaction.

저는 또한 다른 fixture를 정의했습니다. 이 fixture는 boto가 우연히 시스템에서 다른 인증 정보를 선택하는 것을 방지하기 위해 더미 aws 인증 정보를 설정합니다. 테스트를 위해 실제로 'foo'와 'bar'를 자격 증명으로 설정합니다 - 이는 삭제된 정보가 아닙니다.

 

It's important that AWS_PROFILE env be unset because otherwise boto will go looking for that profile.

boto가 그 프로필을 찾아 보기 때문에 AWS_PROFILE 환경 변수가 설정되지 않도록 해야합니다.
@pytest.fixture(scope='session')
def setup_env():
    os.environ['AWS_ACCESS_KEY_ID'] = 'foo'
    os.environ['AWS_SECRET_ACCESS_KEY'] = 'bar'
    os.environ.pop('AWS_PROFILE', None)

 

And then I specify setup_env as a pytest usefixtures entry so that it gets used for every test run.

그런 다음에, 제가 작성한 `setup_env`를 pytest의 `usefixtures` 항목으로 지정해서 모든 테스트 실행에서 이용할 수 있게 했습니다.

 

 

 

출처 : https://stackoverflow.com/questions/37143597/mocking-boto3-s3-client-method-python

반응형
댓글
공지사항
최근에 올라온 글