Search code examples
pythonamazon-web-servicesboto3moto

Test AWS s3 PUT and Download in Pytest


I have the following function.

def test_download(test_args):
    mock = mock_s3()
    mock.start()
    conn = boto3.resource('s3', region_name='us-west-2')
    conn.create_bucket(Bucket=test_args.source_bucket)
    s3.Object(test_args.source_bucket, 'testing.txt').put(
        Body=open("testing.txt", 'rb'))
    handler_client = HandlerClient(test_args)
    handler_client.get_s3_file()

Using from moto import mock_s3

I get the error when I go to test.

    def add_auth(self, request):
        if self.credentials is None:
>           raise NoCredentialsError
E           botocore.exceptions.NoCredentialsError: Unable to locate credentials at line
>       `conn.create_bucket(Bucket=test_args.source_bucket)`

I have tried with the decorator as well as

with moto.mock_s3():

All get the same error. How can I resolve making this issue to create a fake bucket drop an item in it.


Solution

  • moto unfortunately does not mock the authorization parts of boto3. So you need to have some part of the AWS Authorization Chain set up, e.g. exporting AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY. We usually set up a fixture to update the environment before each test:

    @pytest.fixture(scope='function')
    def context():
        context = attrdict.AttrMap()
        orig_env = os.environ.copy()
        os.environ['AWS_ACCESS_KEY_ID'] = 'foo'
        os.environ['AWS_SECRET_ACCESS_KEY'] = 'bar'
        context.os = {'environ': os.environ}
        yield context
        os.environ = orig_env
    

    The import part is setting the AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY, then yielding, then restoring the os.environ back to its original state. You don't have to use the context = attrdict.AttrMap part, we like that so test functions using this fixture have access to the test os.environ (and other test) attributes.