Getting Started with Moto
You can use
pip to install the latest released version of
moto, and specify which service(s) you will use:
pip install moto[ec2,s3,..]
This will install Moto, and the dependencies required for that specific service.
If you don’t care about the number of dependencies, or if you want to mock many AWS services:
pip install moto[all]
If you want to install
moto from source:
git clone git://github.com/spulec/moto.git cd moto python setup.py install
For example, we have the following code we want to test:
import boto3 class MyModel(object): def __init__(self, name, value): self.name = name self.value = value def save(self): s3 = boto3.client('s3', region_name='us-east-1') s3.put_object(Bucket='mybucket', Key=self.name, Body=self.value)
There are several ways to verify that the value will be persisted successfully.
With a decorator wrapping, all the calls to S3 are automatically mocked out.
import boto3 from moto import mock_s3 from mymodule import MyModel @mock_s3 def test_my_model_save(): conn = boto3.resource('s3', region_name='us-east-1') # We need to create the bucket since this is all in Moto's 'virtual' AWS account conn.create_bucket(Bucket='mybucket') model_instance = MyModel('steve', 'is awesome') model_instance.save() body = conn.Object('mybucket', 'steve').get()[ 'Body'].read().decode("utf-8") assert body == 'is awesome'
Same as the Decorator, every call inside the
with statement is mocked out.
def test_my_model_save(): with mock_s3(): conn = boto3.resource('s3', region_name='us-east-1') conn.create_bucket(Bucket='mybucket') model_instance = MyModel('steve', 'is awesome') model_instance.save() body = conn.Object('mybucket', 'steve').get()[ 'Body'].read().decode("utf-8") assert body == 'is awesome'
You can also start and stop the mocking manually.
def test_my_model_save(): mock = mock_s3() mock.start() conn = boto3.resource('s3', region_name='us-east-1') conn.create_bucket(Bucket='mybucket') model_instance = MyModel('steve', 'is awesome') model_instance.save() body = conn.Object('mybucket', 'steve').get()[ 'Body'].read().decode("utf-8") assert body == 'is awesome' mock.stop()
If you use unittest to run tests, and you want to use moto inside setUp, you can do it with .start() and .stop() like:
import unittest from moto import mock_s3 import boto3 def func_to_test(bucket_name, key, content): s3 = boto3.resource('s3') object = s3.Object(bucket_name, key) object.put(Body=content) class MyTest(unittest.TestCase): mock_s3 = mock_s3() bucket_name = 'test-bucket' def setUp(self): self.mock_s3.start() # you can use boto3.client('s3') if you prefer s3 = boto3.resource('s3') bucket = s3.Bucket(self.bucket_name) bucket.create() def tearDown(self): self.mock_s3.stop() def test(self): content = b"abc" key = '/path/to/obj' # run the file which uploads to S3 func_to_test(self.bucket_name, key, content) # check the file was uploaded as expected s3 = boto3.resource('s3') object = s3.Object(self.bucket_name, key) actual = object.get()['Body'].read() self.assertEqual(actual, content)
It is possible to use Moto as a class-decorator. Note that this may behave differently then you might expected - it currently creates a global state on class-level, rather than on method-level.
@mock_s3 class TestMockClassLevel(unittest.TestCase): def create_my_bucket(self): s3 = boto3.resource('s3') bucket = s3.Bucket("mybucket") bucket.create() def test_1_should_create_bucket(self): self.create_my_bucket() client = boto3.client("s3") assert len(client.list_buckets()["Buckets"]) == 1 def test_2_bucket_still_exists(self): client = boto3.client("s3") assert len(client.list_buckets()["Buckets"]) == 1
Stand-alone server mode
Moto also comes with a stand-alone server allowing you to mock out an AWS HTTP endpoint. For testing purposes, it’s extremely useful even if you don’t use Python.
$ moto_server -p3000 * Running on http://127.0.0.1:3000/
However, this method isn’t encouraged if you’re using
boto3, the best solution would be to use a decorator method.
See Non-Python SDK’s / Server Mode for more information.
There are some important caveats to be aware of when using moto:
How do I avoid tests from mutating my real infrastructure
You need to ensure that the mocks are actually in place.
Ensure that your tests have dummy environment variables set up:export AWS_ACCESS_KEY_ID='testing' export AWS_SECRET_ACCESS_KEY='testing' export AWS_SECURITY_TOKEN='testing' export AWS_SESSION_TOKEN='testing'
VERY IMPORTANT: ensure that you have your mocks set up BEFORE your boto3 client is established. This can typically happen if you import a module that has a boto3 client instantiated outside of a function. See the pesky imports section below on how to work around this.
Example on usage
Here is an example:
@pytest.fixture(scope='function') def aws_credentials(): """Mocked AWS Credentials for moto.""" os.environ['AWS_ACCESS_KEY_ID'] = 'testing' os.environ['AWS_SECRET_ACCESS_KEY'] = 'testing' os.environ['AWS_SECURITY_TOKEN'] = 'testing' os.environ['AWS_SESSION_TOKEN'] = 'testing' @pytest.fixture(scope='function') def s3(aws_credentials): with mock_s3(): yield boto3.client('s3', region_name='us-east-1')
In the code sample above, all of the AWS/mocked fixtures take in a parameter of aws_credentials, which sets the proper fake environment variables. The fake environment variables are used so that botocore doesn’t try to locate real credentials on your system.
Next, once you need to do anything with the mocked AWS environment, do something like:
def test_create_bucket(s3): # s3 is a fixture defined above that yields a boto3 s3 client. # Feel free to instantiate another boto3 S3 client -- Keep note of the region though. s3.create_bucket(Bucket="somebucket") result = s3.list_buckets() assert len(result['Buckets']) == 1 assert result['Buckets']['Name'] == 'somebucket'
What about those pesky imports
Recall earlier, it was mentioned that mocks should be established __BEFORE__ the clients are set up. One way to avoid import issues is to make use of local Python imports – i.e. import the module inside of the unit test you want to run vs. importing at the top of the file.
def test_something(s3): from some.package.that.does.something.with.s3 import some_func # <-- Local import for unit test # ^^ Importing here ensures that the mock has been established. some_func() # The mock has been established from the "s3" pytest fixture, so this function that uses # a package-level S3 client will properly use the mock and not reach out to AWS.
Patching the client or resource
If it is not possible to rearrange imports, we can patch the boto3-client or resource after the mock has started. See the following code sample:
# The client can come from an import, an __init__-file, wherever.. client = boto3.client("s3") s3 = boto3.resource("s3") @mock_s3 def test_mock_works_with_client_or_resource_created_outside(): from moto.core import patch_client, patch_resource patch_client(outside_client) patch_resource(s3) assert client.list_buckets()["Buckets"] ==  assert list(s3.buckets.all()) == 
This will ensure that the boto3 requests are still mocked.
For Tox, Travis CI, and other build systems, you might need to also perform a touch ~/.aws/credentials command before running the tests. As long as that file is present (empty preferably) and the environment variables above are set, you should be good to go.