2016-05-10 14 views
23

Ich versuche, eine singluar-Methode aus dem boto3 s3 Client-Objekt zu werfen und Ausnahme zu verspotten. Aber ich brauche alle anderen Methoden für diese Klasse, um normal zu arbeiten.Mocking Boto3 S3-Client-Methode Python

Dies ist, so kann ich eine singuläre Ausnahme Test testen, wenn und Fehler tritt ein upload_part_copy

ersten Versuch

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() 

aber dies gibt den folgenden Fehler ausführen:

ImportError: No module named S3 

2. Versuch

Nachdem am botocore.client.py Quellcode der Suche fand ich, dass es etwas Gescheites und die Methode tut upload_part_copy existiert nicht. Ich fand, dass es scheint, BaseClient._make_api_call statt zu nennen und so versuchte ich zu verspotten, dass

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() 

Dies löst eine Ausnahme aus, sondern löst eine Ausnahme ... aber auf dem get_object das möchte ich vermeiden.

Irgendwelche Ideen, wie ich nur die Ausnahme auf der upload_part_copy Methode werfen?

Antwort

8

Sobald ich hier gepostet habe, gelang es mir, eine Lösung zu finden. Hier ist es hoffe, es hilft :)

import botocore 
from botocore.exceptions import ClientError 
from mock import patch 
import boto3 

orig = botocore.client.BaseClient._make_api_call 

def mock_make_api_call(self, operation_name, kwarg): 
    if operation_name == 'UploadPartCopy': 
     parsed_response = {'Error': {'Code': '500', 'Message': 'Error Uploading'}} 
     raise ClientError(parsed_response, operation_name) 
    return orig(self, operation_name, kwarg) 

with patch('botocore.client.BaseClient._make_api_call', new=mock_make_api_call): 
    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() 

Jordan Philips also posted a great solution die die botocore.stub.Stubber Klasse. Während einer saubereren Lösung konnte ich bestimmte Operationen nicht vortäuschen.

+2

Dies ist sehr hilfreich. Es hat eine Weile gedauert, bis ich festgestellt habe, dass viele der boto3-Clients effektiv [zur Laufzeit generiert] (https://boto3.readthedocs.io/en/latest/guide/new.html#major-features) und als solche erstellt wurden , kann nicht direkt verspottet werden. – rumdrums

32

Botocore hat einen Client Glutabstreifer Sie nur für diesen Zweck verwendet werden können: docs.

Hier ist ein Beispiel in einem Fehler des Setzens:

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() 

Hier ein Beispiel ist in eine normale Antwort des Setzens Zusätzlich können die Glutabstreifer nun in einem Kontext verwendet werden.. Es ist wichtig zu beachten, dass der Stubber soweit wie möglich verifizieren wird, dass die angegebene Antwort mit dem übereinstimmt, was der Dienst tatsächlich zurückgibt. Das ist nicht perfekt, aber es schützt Sie davor, totale Nonsense-Antworten einzufügen.

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 
+0

Vielen Dank @Jordan, warum konnte ich das nicht finden? – ptimson

+0

Nun, da es in Botocore ist, müssten Sie in den Botocore-Dokumenten suchen, und nicht viele tun. Es ist auch ziemlich neu. –

+0

Warum wird client.upload_part_copy() einen ClientError auslösen? –

5

Hier ist ein Beispiel für einen einfachen Python Unittest das kann zu fälschen client = boto3.client verwendet werden ('EC2') api Anruf ...

import boto3 

class MyAWSModule(): 
    def __init__(self): 
     client = boto3.client('ec2') 
     tags = client.describe_tags(DryRun=False) 


class TestMyAWSModule(unittest.TestCase): 
    @mock.patch("boto3.client.get_tags") 
    @mock.patch("boto3.client") 
    def test_open_file_with_existing_file(self, mock_boto_client, mock_describe_tags): 
     mock_boto_client.return_value = mock_get_tags_response 
     my_aws_module = MyAWSModule() 

     mock_boto_client.assert_call_once('ec2') 
     mock_describe_tags.assert_call_once_with(DryRun=False) 

mock_get_tags_response = { 
    'Tags': [ 
     { 
      'ResourceId': 'string', 
      'ResourceType': 'customer-gateway', 
      'Key': 'string', 
      'Value': 'string' 
     }, 
    ], 
'NextToken': 'string' 
} 

hoffentlich das hilft.

+0

Wie kann ich ein globales Client- oder Ressourcenobjekt verwalten? Dies kann nicht verspottet werden, da der Aufruf vor der Mock-Einrichtung erfolgt. – pt12lol

+0

erste Zeile ‚test_open_file_with_existing_file‘ sollte nicht ‚mock_describe_tags.return_value = mock_get_tags_response‘ sein? anstelle von 'mock_boto_client'? – daniele