在Python中将不同的值传递给单元测试的装饰器

在Python中将不同的值传递给单元测试的装饰器,python,unit-testing,decorator,python-decorators,Python,Unit Testing,Decorator,Python Decorators,在我的一个类方法中,我试图修改传递给装饰器的参数。代码如下所示: class MyClass(object): @tryagain(retries=3) def mymethod(self, arg): ... do stuff ... @tryagain(retries=self.retries) def mymethod(self, arg): ... do stuff ... 我的问题是,在运行单元测试时,我想将“retries”变量更改为小于3的值,但

在我的一个类方法中,我试图修改传递给装饰器的参数。代码如下所示:

class MyClass(object):
  @tryagain(retries=3)
  def mymethod(self, arg):
    ... do stuff ...
  @tryagain(retries=self.retries)
  def mymethod(self, arg):
    ... do stuff ...
我的问题是,在运行单元测试时,我想将“retries”变量更改为小于3的值,但对于生产代码,将其保持为“3”。不幸的是,看起来我做不到这样的事情:

class MyClass(object):
  @tryagain(retries=3)
  def mymethod(self, arg):
    ... do stuff ...
  @tryagain(retries=self.retries)
  def mymethod(self, arg):
    ... do stuff ...

因为类在参数传递给装饰器的时候没有定义(据我所知)

我还尝试在模块中添加变量,如下所示:

retries = 1
def MyClass(object):
    @tryagain(retries=retries)
    def mymethod(self, arg):
      ... do stuff ...

但是,我似乎无法在单元测试中修改“重试”的值。有没有其他方法可以完成我想做的事情?

我假设您尝试减少重试次数以提高测试速度

如果是这样,修改重试次数变量似乎不是最好的方法。相反,您可以先在不使用decorator的情况下对函数
mymethod
进行单元测试,然后创建
mymethod
的模拟函数。让我们称它为
mock_mymethod
,用
@tryagain
装饰它,并测试“tryagain”的逻辑是否真的有效


检查模块以了解如何创建模拟实例,这篇关于
mock
的文章也值得一读。

您可以使用从调用代码设置的环境变量(在这里输入默认值可能会更好)

import os
# ...
class MyClass(object):
    @tryagain(retries=int(os.environ['project_num_retries']))
    def mymethod(self, arg):
        print("mymethod")
或者使用“全局”类型的模块,例如:
project\u settings.py
包含:

num_retries = 3
然后

但我不确定用测试信息装饰代码是您真正应该做的事情——那么:

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

注意,我使用了下面的
@tryagain
装饰器的简单示例,您的示例可能更复杂,需要对示例进行一些调整:

def tryagain(retries):
    def wrap(f):
        def wrapped_f(*args,**kwargs):
            for _ in xrange(retries):
                f(*args,**kwargs)
        return wrapped_f
    return wrap

是的,这是正确的。我想我的下一个问题是,没有decorator我如何测试mymethod?看起来我可以修补decorator,但它不是那么简单。我必须仔细阅读。尝试mock
tryagain
并将其返回值设置为相同的输入函数,然后测试
mymethod
。这实际上是w没有任何装饰效果。在那之后,mock
mymethod
和test
tryagain
。我会尝试一下。如果你认真对待unittest,mock绝对值得学习,我认为它非常适合你在这种特殊情况下的需要。是的,我第一次在这个项目上使用mock,所以我有点熟悉,b但我仍在学习一些东西。昨晚我终于能够在一些工作之后找到正确修补装饰器的地方,我认为这是一个方向。我认为设置环境变量将满足我的需要。可能还不清楚,但我正在尝试绕过(或缩短)我运行单元测试所需的时间。我希望@tryagain修饰生产代码,因为我希望在第一次失败时再次调用“mymethod”。我不希望在运行测试时启动它,因为在测试某些失败案例时,需要很长时间才能恢复(它有一个回退时间).Oh,所以
@tryagain
不是测试修饰符,而是在测试时要修改参数的生产修饰符,明白了。忽略答案的第二部分。
def tryagain(retries):
    def wrap(f):
        def wrapped_f(*args,**kwargs):
            for _ in xrange(retries):
                f(*args,**kwargs)
        return wrapped_f
    return wrap