Mocking Boto3 with pytest

Posted 25 July 2023


When writing unit tests that interact with AWS services like S3 or DynamoDB using the boto3 Python library you'll want to mock those requests. You can use the botocore Stubber to achieve this.

Given a function that queries DynamoDB for an item:

import boto3

def get_item(id):
  dynamodb = boto3.client("dynamodb")
  response = dynamodb.get_item(
    TableName="item_table",
    Key={"id": id}
  )
  return response.get("Item")

You can write a pytest unit test using botocore.stub to mock the requests:

import boto3
from botocore.stub import Stubber

from my_project import get_item

def test_get_item(mocker):
  dynamodb = boto3.client("dynamodb")
  stubber = Stubber(dynamodb)

  stubber.add_response(
    "get_item",
    {"hello": "world"},
    {"TableName": "item_table", "Key": {"id": "hello-world"}}
  )

  with mocker.patch(
    "boto3.client", return_value=dynamodb
  ):
    with stubber:
      result = get_item("hello-world")

      assert result["hello"] == "world"
  • The add_response call adds a mock response of {"hello": "world"} for a get_item call, as well as asserting the expected arguments given
  • with stubber: activates the Stubber - you can also call stubber.activate() after setting up mock responses

You can also use the stubber with the boto3.resource API:

import boto3

def get_item(id):
  dynamodb = boto3.resource("dynamodb")
  table = dynamodb.Table("item_table")
  response = table.get_item(
    Key={"id": id}
  )
  return response.get("Item")
import boto3
from botocore.stub import Stubber

from my_project import get_item

def test_get_item(mocker):
  dynamodb = boto3.resource("dynamodb")
  stubber = Stubber(dynamodb.meta.client) # Access the client through `meta`

  stubber.add_response(
    "get_item",
    {"hello": "world"},
    {"TableName": "item_table", "Key": {"id": "hello-world"}}
  )

  with mocker.patch(
    "boto3.resource", return_value=dynamodb
  ):
    with stubber:
      result = get_item("hello-world")

      assert result["hello"] == "world"

Further reading


Related posts

Configure Boto3 endpoint_url

Published

Use environment variables to point Boto3 at local services

Python TypedDict with Generics

Published

How to define a Python type for a dict with generic values

Contract testing with OpenAPI & TypeScript

Published

Validate contracts between backend services and frontend applications


Thanks for reading

I'm Alex O'Callaghan and this is my personal website where I write about software development and do my best to learn in public. I currently work at Mintel as a Principal Engineer working primarily with React, TypeScript & Python.

I've been leading one of our platform teams, first as an Engineering Manager and now as a Principal Engineer, maintaining a collection of shared libraries, services and a micro-frontend architecture.