Infrastructure as Code (IaC) - Unit Testing Python Code for AWS (Boto3)

The heading is a mouthful, but the approach I want to demonstrate is rather simplistic.

Recently, I was looking at better ways to unit test IaC code I wrote in Python targeting AWS through the excellent boto3 library.

In this post, I explore a potential testing approach, using the built in mocking functionality of the Python unittest framework.

Testing it out...

The examples provided will aim to mock a call to AWS S3 API to see if a particular bucket exists, and if it doesn't, create it. There are therefore potentially two API calls we need to make using the Boto3 Library.

I created a repository on GitHub and all code examples below are from that repository.

As with most Python projects, I recommend using a virtual environment and install boto3. You can do this with the following commands on a Terminal:

$ python -m venv venv
$ . venv/bin/activate
$ pip3 install boto3 coverage

Your shell prompt may get an additional label like (venv) that indicates you are now in that virtual environment.

Technically speaking, I could have just used the function to create the bucket and handle the error appropriately in case a bucket already exists, so please consider this purely on educational merits with the aim to try and learn more about mocking Boto3 and AWS API calls.

To run the test, in the cloned directory, execute the following command:

coverage run -m unittest provision_s3_bucket_v1.py

Hopefully all tests will pass. To get a quick view on our actual code coverage, run the following command:

coverage report -m

At the time of writing this blog post, the output was as follow:

Name                        Stmts   Miss  Cover   Missing
---------------------------------------------------------
provision_s3_bucket_v1.py      96      1    99%   32
---------------------------------------------------------
TOTAL                          96      1    99%

The line that was not covered can easily be included, but I kept it like this just to give an example of the output.

So this guarantees it will work then...

Well.... No.

Have a look at the original code for the create_s3_bucket() function:

def create_s3_bucket(client, bucket_name: str)->str:
    response = client.create_bucket(Bucket=bucket_name)
    return response['Location']

With the code above, all tests will still pass, but if you are creating the bucket anywhere other than us-east-1, the script will fail with an exception:

botocore.exceptions.ClientError: An error occurred (IllegalLocationConstraintException) when calling the CreateBucket operation: The unspecified location constraint is incompatible for the region specific endpoint this request was sent to.

This raise several important points about Unit Testing:

Conclusion

I hope you enjoyed this post (if you read this far) and that you also learned something. Please feel free to also share your thoughts in the comments below.