Over the last few years, I’ve written a number of different Lambda functions in Python for keeping my AWS environment clean and to delete unused resources. Writing unit tests for my code has always been a weakness, and even more so for projects that require mocking resources like AWS. Recently, I was working on a project to deregister AMIs that are no longer being used by running instances, so I decided to use the opportunity to force myself to write some proper tests and learn how to mock out remote infrastructure. Fortunately, I came across moto.
What is Moto
Moto is a Python library that allows you to easily mock out tests based on AWS Infrusturcture. It allows you to mock out over 30 different AWS services, including all of the most popular services such as EC2, S3, RDS, IAM, etc.
You can install moto via pip:
pip install moto
Writing a moto test for EC2
I had a trouble finding examples for the services I wanted to mock, so it took me a while to work it all out (and keep in mind testing is not my strong suit, so these will likely get better over time). Most of the examples were for S3 endpoints, and I wanted to test various EC2 commands. After looking at some other examples, I was able to cobble together something that works.
I started by creating the mock ec2 connection for boto3. I did this by creating a pytest fixture that yields the connection to be used in the actual test functions.
@pytest.fixture(scope="module") def ec2_client(): mock = mock_ec2() mock.start() conn = boto3.client('ec2', 'us-east-2') yield conn mock.stop()
Next, I wrote the test I wanted, using the
@mock_ec2 decorator and passing the ec2_client to function. In the example function, I am creating an AMI, launching an instance based on that AMI, and then checking that an instance is running based on that AMI.
@mock_ec2 def test_get_instances_list(ec2_client): ''' Tests that an instance created with the image returns in the list ''' image_reservation = ec2_client.run_instances(ImageId="ami-03cf127a", MinCount=1, MaxCount=1) instance = reservation['Instances']['InstanceId'] image_id = ec2_client.create_image(InstanceId=instance, Name="test-ami", Description="This is a test ami") reservation = ec2_client.run_instances(ImageId=image_id, MinCount=1, MaxCount=1) running_instances = app.get_instances(ec2_client) # Assertions assert running_instances.get(reservation['Instances']['InstanceId'], None) == image_id
I still have a lot to learn, but I’m excited to get better at writing tests and learning moto more deeply.