How do I mock boto3 methods called from within a custom class' custom method?

Viewed 1555

I have a python file file/s3.py with custom class S3 (capitalized), which has method get_list_of_objects(). Within this method there are chained boto3 methods called, self.bucket.objects.filter()

How do I mock these boto3 methods?

project_dir/file/s3.py

import boto3
import botocore
import pandas


class S3:
    def __init__(self, bucket_name="my_bucket_of_stuff"):
        self.s3_resource = boto3.resource("s3")
        self.bucket = self.s3_resource.Bucket(bucket_name)
        self.s3_client = boto3.client("s3")
        self._bucket_name = bucket_name

    def get_list_of_objects(self, key_prefix):
        key_list = []
        for object_summary in self.bucket.objects.filter(Prefix=key_prefix):  #mock this bucket.objects.filter
            key_list.append(object_summary.key)
        return key_list

My attempt so far project_dir/tests/test.py

import unittest
import file.s3
from unittest.mock import patch
from file.s3 import S3


class TestS3Class(unittest.TestCase):
    """TestCase for storage/s3.py"""

    def setUp(self):
        """Creates an instance of the live S3 class for testing"""
        self.s3_test_client = S3()


    @patch('file.s3.S3.bucket')
    @patch('file.s3.S3.bucket.objects')
    @patch('file.s3.S3.bucket.objects.filter')
    def test_get_list_of_objects(self, mock_filter, mock_objects, mock_bucket):
        """Asserts retrieved dictionary of S3 objects is processed and returned as type list"""
        mock_bucket.return_value = None
        mock_objects.return_value = None
        mock_filter.return_value = {'key': 'value'}
        self.assertIsInstance(self.s3_test_client.get_list_of_objects(key_prefix='key'), list)

Which yields error ModuleNotFoundError: No module named 'file.s3.S3'; 'file.s3' is not a package

I must be messing up the patch location but I'm not sure how.

1 Answers

I found it easier to mock the boto3.resource class than to try to mock those methods from within my custom class. So, the working test code is:

@patch('boto3.resource')
    def test_get_list_of_objects(self, mock_resource):
        """Asserts retrieved dictionary of S3 objects is processed and returned as type list"""
        mock_resource.Bucket.return_value = {'key': 'value'}

        self.assertIsInstance(self.s3_test_client.get_list_of_objects(key_prefix='key'), list)

I do now get a warning but the test passes and fails correctly. ResourceWarning: unclosed <ssl.SSLSocket....

Related