Home > database >  AttributeError on Mocked Class in Python
AttributeError on Mocked Class in Python

Time:10-09

I am using mockito to unit test a program in Python. I have a class like:

import boto3
import datetime

class Cache:

    def __init__(self):
        client = boto3.resource('s3')
        self.bucket_name = 'name'
        self.bucket = client.Bucket(self.bucket_name)

    def setup_cache(self, cache_file='cache.csv', cache_filepath='cache'):
        cache_object = self.bucket.Object(cache_file)

        if cache_object.last_modified < datetime.datetime.now():
            self.bucket.download_file(cache_filepath, cache_file)
        else:
            print('Cache already up to date')


def main():
    cache = Cache()
    cache.setup_cache()

And the test code I am getting stuck on is this:

from mockito import mock, when
import datetime
import boto3

import mock_cache

class TestMockCache:

    def test_last_mod(self):

        mock_client = mock()
        when(boto3).resource('s3').thenReturn(mock_client)

        mock_bucket = mock()
        when(mock_client).Bucket('name').thenReturn(mock_bucket)

        mock_bucket.last_modified = datetime.datetime.now()

        mock_cache.main()

When running pytest on the unit test, I am getting thrown this attribute error:

AttributeError: 'NoneType' object has no attribute 'last_modified'

From the documentation it looked like I could assign 'cache_mock.last_modified' like this. However, I also tried:

when(cache_mock).last_modified.thenReturn(test_date)

and got:

AttributeError: 'StubbedInvocation' object has no attribute 'thenReturn'

Which I don't fully understand, but assume that means a mockito mock() object can't have multiple return values?

Any help with this would be appreciated. I feel like I am misunderstanding something fundamental about either how mockito's mock works or mocking in general.

CodePudding user response:

The patch from the Mock can be used to mock the client from boto3. Do not have to return anything from the client, as it is being mocked on the whole.

e.g.:

from folder.file import your_func
from unittest.mock import patch

class TestSomething(unittest.TestCase):
    @patch("botocore.client.BaseClient._make_api_call")
    def test_something(self, _mock_client_boto3):
        your_func()
        .. do something

In case you want to return something from the client, it could be achieved by specifying the return_value in the patch as shown below:

from folder.file import your_func
from unittest.mock import patch

class TestSomething(unittest.TestCase):
    @patch("botocore.client.BaseClient._make_api_call", return_value=None)
    def test_something(self, _mock_client_boto3):
        your_func()
        .. do something
  • Related