Python 中 Mocked Class 的 AttributeError

AttributeError on Mocked Class in Python

我正在使用 mockito 对 Python 中的程序进行单元测试。我有一个 class 像:

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()

我遇到的测试代码是这样的:

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()

当运行 pytest 进行单元测试时,我会抛出这个属性错误:

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

从文档看来我可以像这样分配 'cache_mock.last_modified'。不过,我也试过了:

when(cache_mock).last_modified.thenReturn(test_date)

并得到:

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

我不完全理解,但假设这意味着 mockito mock() 对象不能有多个 return 值?

如有任何帮助,我们将不胜感激。我觉得我误解了一些关于 mockito 的模拟如何工作或一般模拟的基本知识。

Mock 中的 patch 可用于模拟 boto3 中的 client。不需要return客户端的任何东西,因为它在整体上被嘲笑了。

例如:

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

如果您想从客户端 return 获取某些内容,可以通过在补丁中指定 return_value 来实现,如下所示:

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