【问题标题】:Pytest unit test fails because target function has cachetools.ttl_cache decoratorPytest 单元测试失败,因为目标函数有 cachetools.ttl_cache 装饰器
【发布时间】:2018-01-09 13:14:21
【问题描述】:

我有一个函数,我正在编写使用 pytest 的单元测试。唯一的问题是,由于我正在为同一个函数编写多个测试,因此有几个测试由于 cachetools.ttl_cache 装饰器而失败。这个装饰器使函数每次运行时都返回相同的值,这会弄乱测试。这个装饰器不存在于我正在测试的函数中,而是存在于由我正在测试的函数调用的函数中。我无法从我正在测试的函数中删除这个装饰器。这是测试:

@patch('load_balancer.model_helpers.DBSession')
def test_returns_true_if_split_test_is_external(self, dbsession, group_ctx):
    group_ctx.group_id = '{}-{}'.format('2222222222', '123456789')
    split_test = Mock()
    split_test.state = 'external'
    config = {
        'query.return_value.filter.return_value.first.return_value': split_test
    }
    dbsession.configure_mock(**config)
    assert group_ctx.is_in_variation_group('foo') == True

这里是要测试的功能:

def is_in_variation_group(self, split_test=None):
    try:
        split_test = get_split_test(split_test) # This function has the 
        #decorator
        log.info('Split test {} is set to {}'.format(split_test.name,
                                                     split_test.state))
        if not split_test or split_test.state == 'off':
            return False

        phone_number = int(self.group_id.split('-')[0])
        if split_test.state == 'internal':
            return True if str(phone_number) in INTERNAL_GUINEA_PIGS else False
        if split_test.state == 'external':
            return True if phone_number % 2 == 0 else False
    except Exception as e:
        log.warning("A {} occurred while evaluating membership into {}'s variation "
                    "group".format(e.__class__.__name__, split_test))

获取拆分测试函数:

    @cachetools.ttl_cache(maxsize=1024, ttl=60)
    def get_split_test(name):
         return (DBSession.query(SplitTest)
                 .filter(SplitTest.name == name)
                 .first())

我怎样才能让这个缓存装饰器被忽略?非常感谢任何帮助

【问题讨论】:

  • 我认为您需要在运行测试之前修补 cachetools 并将 ttl_cache 替换为不缓存任何内容的函数。

标签: python unit-testing mocking pytest


【解决方案1】:

我建议在每次测试运行后清除函数的缓存。

cachetools documentation 没有提到这一点,但从the source code 看来,缓存装饰器公开了一个cache_clear 函数。

对于您正在测试的示例代码:

import cachetools.func

@cachetools.func.ttl_cache(maxsize=1024, ttl=60)
def get_split_test(name):
     return (DBSession.query(SplitTest)
             .filter(SplitTest.name == name)
             .first())

这将是我的方法(假设 pytest >= 3,否则使用 yield_fixture 装饰器):

@pytest.fixture(autouse=True)
def clear_cache():
    yield
    get_split_test.cache_clear()

def test_foo():
    pass # Test your function like normal.

clear_cache 夹具使用在每次测试后自动使用的产量夹具 (autouse=True) 在每次测试后执行清理。您也可以使用the request fixture and request.addfinalizer 来运行清理功能。

【讨论】:

  • 感谢您的建议,但是当我在上面运行该夹具时出现此错误:AttributeError: 'function' object has no attribute 'clear_cache' 还有其他方法可以清除缓存吗?
  • 哎呀,我把它颠倒过来了,它是cache_clear()。我会更新我的答案。顺便说一句,除非您运行的是不同版本的 cachetools,否则您的示例应该将 cachetools.func.ttl_cache 显示为装饰器。我测试了cachetools 2.0.0版
  • 你一针见血。非常感谢
  • 注意cachetools版本3.0.0方法是.clear()而不是cache_clear()
【解决方案2】:

我会将缓存分配给一个变量,然后在测试结束时重置缓存

GET_USERS_CACHE = TTLCache(maxsize=128, ttl=60)

@cached(cache=GET_USERS_CACHE)
def get_users():
    pass

测试文件

def test_get_users():
    # test code here
    GET_USERS_CACHE.clear()

【讨论】:

    猜你喜欢
    • 2021-04-09
    • 1970-01-01
    • 1970-01-01
    • 2016-12-30
    • 2013-07-06
    • 2011-03-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多