Replacing boto S3 mocks using moto in Python

Let's say you have some Python application code which connects to Amazon S3 which retrieves the keys in a bucket. Very likely, the application would be using boto and the code would like this:

import boto

def get_s3_conn():
    return boto.connect_s3('<aws-access-key', '<aws-secret-key>')

def list_keys():
    s3_conn = get_s3_conn()
    b = s3_conn.get_bucket('bucket_name')
    keys = b.list()
    return keys

The corresponding test would presumably use some mocks and patching. Here is one way to write a test for the above code:

# Assume the code above is in a module list_keys
# in a function list_keys

from list_keys import list_keys

from mock import patch, Mock

def test_list_keys():
   mocked_keys = [Mock(key='mykey1'), Mock(key='key2')]
   mocked_connection = Mock()
   # Start with patching connect_s3
   with patch('boto.connect_s3', Mock(return_value=mocked_connection)):
       mocked_bucket = Mock()
       # Mock get_bucket() call
       mocked_connection.get_bucket = Mock(return_value=mocked_bucket)
       # Mock the list() call to return the keys you want
       mocked_bucket.list = Mock(return_value=mocked_keys)
       keys = list_keys()

       assert keys == mocked_keys

I thought I really had no other way to get around mocks and patches if I wanted to test this part of my application. But, I discovered moto. Then life became easier.

Using moto's S3 support, I don't need to worry about the mocking and patching the boto calls any more. Here is the same test above, but using moto:

from list_keys import get_s3_conn, list_keys
from moto import mock_s3

def test_list_keys():

    expected_keys = ['key1', 'key2']

    moto = mock_s3()
    # We enter "moto" mode using this
    moto.start()

    # Get the connection object
    conn = get_s3_conn()

    # Set up S3 as we expect it to be
    conn.create_bucket('bucket_name')
    for name in expected_keys:
        k = conn.get_bucket('bucket_name').new_key(name)
        k.set_contents_from_string('abcdedsd')

    # Now call the actual function
    keys = list_keys()
    assert expected_keys == [k.name for k in keys]

    # get out of moto mode
    moto.stop()

Unless it is obvious, here are two major differences from the previous test:

We don't mock or patch anything

The point #1 above is the direct reason I would consider using moto for testing S3 interactions rather than setting up mocks. This helps us in the scenario in which this section of the code lies in another package, not the one you are writing tests for currently. You can actually call this section of the code and let the interaction with S3 happen as if it were interacting directly with Amazon S3. I think this allows deeper penetration of your tests and as a result your code's interactions with others.

The test code has to explicitly first setup the expected state

This may seem like more work, but I think it still outweighs the benefits as mentioned previously.

Please checkout moto here.

If you like this post, please follow PythonTestTips on Twitter.