在 Python 中将不同的值传递给装饰器以进行单元测试
Passing different values to decorators for unit tests in Python
我有一种情况,我试图修改传递给我的 class 方法之一的装饰器的参数。代码看起来像这样:
class MyClass(object):
@tryagain(retries=3)
def mymethod(self, arg):
... do stuff ...
我的问题是我想在 运行 我的单元测试时将 "retries" 变量更改为小于 3 的值,但将其保持在“3”为生产代号。不幸的是,看起来我不能做这样的事情:
@tryagain(retries=self.retries)
def mymethod(self, arg):
... do stuff ...
或
@tryagain(retries=MyClass.retries)
def mymethod(self, arg):
... do stuff ...
因为 class 没有在参数传递给装饰器时定义(据我所知)。
我也试过像这样在模块中添加变量:
retries = 1
def MyClass(object):
@tryagain(retries=retries)
def mymethod(self, arg):
... do stuff ...
但是我似乎无法从我的单元测试中修改 "retries" 的值。还有其他方法可以完成我想做的事情吗?
我假设您尝试减少重试次数以提高测试速度。
如果是这样,修改重试次数变量似乎不是最好的方法。相反,您可以先在没有装饰器的情况下对函数 mymethod
进行单元测试,然后创建 mymethod
的模拟函数。我们称它为 mock_mymethod
,用 @tryagain
装饰它并测试 `tryagain 的逻辑是否真的有效。
检查 mock
module to see how to create a mock instance, this article 关于 mock
也值得一读。
您可以使用一个环境变量,从您的调用代码中设置(最好在此处设置一个默认值
import os
# ...
class MyClass(object):
@tryagain(retries=int(os.environ['project_num_retries']))
def mymethod(self, arg):
print("mymethod")
或者使用"globals"类型的模块,例如:project_settings.py
包含:
num_retries = 3
然后
import project_settings
class MyClass(object):
@tryagain(retries=project_settings.num_retries)
def mymethod(self, arg):
print("mymethod")
但我不确定用测试信息装饰您的代码是您真正应该做的事 -- 怎么样:
class MyClass(object):
def mymethod(self, arg):
print("mymethod")
然后像 unittests.py
:
DEV_TESTS = True # Change to False for production
num_retries = 3 if not DEV_TESTS else 1
import <your class>
class UnitTests():
def __init__(self):
self.c = <your_class>.MyClass()
@tryagain(retries=num_retries)
def test_mymethod(self):
self.c.mymethod("Foo")
t = UnitTests()
t.test_mymethod()
如果您愿意,这个 unittests.py
可以与类似 python 的 unittest
package 一起使用:
DEV_TESTS = True # Change to False for production
num_retries = 3 if not DEV_TESTS else 1
import unittest
import <your class>
class UnitTests(unittest.TestCase):
def setUp(self):
self.c = <your class>.MyClass()
@tryagain(retries=num_retries)
def test_mymethod(self):
self.c.mymethod("Foo")
注意,我使用了以下 @tryagain
装饰器的简单示例,您的可能更复杂,需要对示例进行一些调整:
def tryagain(retries):
def wrap(f):
def wrapped_f(*args,**kwargs):
for _ in xrange(retries):
f(*args,**kwargs)
return wrapped_f
return wrap
我有一种情况,我试图修改传递给我的 class 方法之一的装饰器的参数。代码看起来像这样:
class MyClass(object):
@tryagain(retries=3)
def mymethod(self, arg):
... do stuff ...
我的问题是我想在 运行 我的单元测试时将 "retries" 变量更改为小于 3 的值,但将其保持在“3”为生产代号。不幸的是,看起来我不能做这样的事情:
@tryagain(retries=self.retries)
def mymethod(self, arg):
... do stuff ...
或
@tryagain(retries=MyClass.retries)
def mymethod(self, arg):
... do stuff ...
因为 class 没有在参数传递给装饰器时定义(据我所知)。
我也试过像这样在模块中添加变量:
retries = 1
def MyClass(object):
@tryagain(retries=retries)
def mymethod(self, arg):
... do stuff ...
但是我似乎无法从我的单元测试中修改 "retries" 的值。还有其他方法可以完成我想做的事情吗?
我假设您尝试减少重试次数以提高测试速度。
如果是这样,修改重试次数变量似乎不是最好的方法。相反,您可以先在没有装饰器的情况下对函数 mymethod
进行单元测试,然后创建 mymethod
的模拟函数。我们称它为 mock_mymethod
,用 @tryagain
装饰它并测试 `tryagain 的逻辑是否真的有效。
检查 mock
module to see how to create a mock instance, this article 关于 mock
也值得一读。
您可以使用一个环境变量,从您的调用代码中设置(最好在此处设置一个默认值
import os
# ...
class MyClass(object):
@tryagain(retries=int(os.environ['project_num_retries']))
def mymethod(self, arg):
print("mymethod")
或者使用"globals"类型的模块,例如:project_settings.py
包含:
num_retries = 3
然后
import project_settings
class MyClass(object):
@tryagain(retries=project_settings.num_retries)
def mymethod(self, arg):
print("mymethod")
但我不确定用测试信息装饰您的代码是您真正应该做的事 -- 怎么样:
class MyClass(object):
def mymethod(self, arg):
print("mymethod")
然后像 unittests.py
:
DEV_TESTS = True # Change to False for production
num_retries = 3 if not DEV_TESTS else 1
import <your class>
class UnitTests():
def __init__(self):
self.c = <your_class>.MyClass()
@tryagain(retries=num_retries)
def test_mymethod(self):
self.c.mymethod("Foo")
t = UnitTests()
t.test_mymethod()
如果您愿意,这个 unittests.py
可以与类似 python 的 unittest
package 一起使用:
DEV_TESTS = True # Change to False for production
num_retries = 3 if not DEV_TESTS else 1
import unittest
import <your class>
class UnitTests(unittest.TestCase):
def setUp(self):
self.c = <your class>.MyClass()
@tryagain(retries=num_retries)
def test_mymethod(self):
self.c.mymethod("Foo")
注意,我使用了以下 @tryagain
装饰器的简单示例,您的可能更复杂,需要对示例进行一些调整:
def tryagain(retries):
def wrap(f):
def wrapped_f(*args,**kwargs):
for _ in xrange(retries):
f(*args,**kwargs)
return wrapped_f
return wrap