我正在尝试测试一个装饰类方法:
class S3Store(object):
@retry(exceptions=Exception, delay=1, tries=5, backoff=2)
def delete(self, dest_id):
return self._delete(dest_id=dest_id)
def _delete(self, dest_id):
bucket = self.conn.get_bucket(get_bucket_from_s3_uri(dest_id))
key = Key(bucket, get_key_from_s3_uri(dest_id))
key.delete()
我已经嘲笑并测试了_delete
,现在我想测试重试逻辑。
我不能直接测试delete()
,因为Key
不会被嘲笑。所以我希望做的事情如下:
decorated_fn = retry.retry_decorator(storage_backend._delete, delay=0.00001)
storage_backend.delete = decorated_fn
storage_backend.delete(...) ... # add assertions, etc.
这不起作用。我收到一个错误:
AttributeError: 'function' object has no attribute 'retry_decorator'
我认为问题在于retry
decorator本身就是装饰的。
如何在我的delete()
方法上测试重试逻辑,以便可以模拟其内部对象,因此延迟超时非常低?
答案 0 :(得分:0)
您不应该在delete函数中测试重试装饰器,而是使用测试函数来测试重试装饰器。
def test_retry(self):
@retry(exceptions=ValueError, delay=1, tries=5, backoff=2)
def test_raise_wrong_exception():
raise AssertionError()
self.assertRaises(AssertionError, test_raise_wrong_exception)
...
答案 1 :(得分:0)
装饰器是一个函数,它将一个函数作为参数,并返回一个装饰版本。
你的情况很混乱,因为它包含很多嵌套。让我们首先刷新装饰器的语法:
当我们写:
@decorator
def fun():
pass
这相当于:
def fun():
pass
fun = decorator(fun)
在您的示例中,retry
函数实际上不是装饰器,而是创建装饰器:
@decorator_factory(...)
def fun():
pass
相当于:
def fun():
pass
decorator = decorator_factory(...)
fun = decorator(fun)
现在显然你想要:
decorator = retry(delay=0.00001)
decorated_fn = decorator(storage_backend._delete)
如果我们查看源代码,看起来retry_decorator
实际上并不是装饰器:它返回f
的结果,而不是具有增强行为的新函数:
@decorator
def retry_decorator(f, *args, **kwargs):
for i in count():
try:
return f(*args, **kwargs)
except exceptions, e:
if i >= tries:
raise
round_delay = delay * backoff ** i
log.warning('%s, retrying in %s seconds...', e, round_delay)
time.sleep(round_delay)
但是@decorator
会将retry_decorator
转换为实际的装饰者see here。