2014-12-01 54 views
0

我写了一个正确工作的装饰器,但是我偶然发现了试错法的正确解决方案,而且我的关于装饰器的知识告诉我某些东西没有很好的定义。了解我自己的装饰器

这种情况是我嘲笑一个Rest Api来做一些TDD,而这个Rest是一个令牌安全。所以在提出任何请求之前,我首先必须获得我的用户令牌。我使用httpretty来模拟API。

到目前为止,我不得不在每个测试用例中register_uri,一个用于模拟/ token资源,另一个用于测试任何其他资源。但是我发现这非常麻烦,所以有了一个解决方案来写一个简单的装饰器来模拟/标记,然后只需要嘲笑测试资源。

这是我目前正在装饰...

def activate_security(func): 
    def test_case(test_case): 
     httpretty.enable() 
     uri = 'http://{}:{}/token'.format(HOST, PORT) 
     httpretty.register_uri(httpretty.GET, uri, 
           body=dumps({'token': 'dummy_token'}), 
           content_type='application/json') 
     test_case() 
     httpretty.disable() 
    return test_case 

这是怎么叫。

@activate_security 
@httpretty.activate 
def test_case_one(self): 
    #Test case here 

我不得不通过test_case参数的内部函数“原因没有它不会工作,这是test_case方法test_case_one,我认为这将在FUNC参数传递,但在FUNC外部作用域将对象保存在test_case的内存中。

应该不是func装饰器的返回值吗?如果我这样做,装饰者不起作用。当内部函数传递该参数?

回答

2

您正在装饰方法,因此您的结果包装函数需要self参数,就像在类中使用普通函数一样。

所有不同的是,您使用了不同的名称来回self参数test_case。碰巧,实例是可调用的,并调用它来运行测试,因此您实质上正在执行self()以再次运行测试

仅举参数self,并把它传递给被包装的函数:

def activate_security(func): 
    def wrapper(self): 
     httpretty.enable() 
     uri = 'http://{}:{}/token'.format(HOST, PORT) 
     httpretty.register_uri(httpretty.GET, uri, 
           body=dumps({'token': 'dummy_token'}), 
           content_type='application/json') 
     func(self) 
     httpretty.disable() 
    return wrapper 

然后wrapper()功能取代了原来的test_case_one功能,并运行测试时wrapper()功能绑定到测试用例实例并将通过该实例作为self;在您的包装中,您可以通过简单地将self传递给它来调用未绑定的func()

为了调试的目的,将一些函数属性从wrap函数复制到wrapper通常更好些; @functools.wraps() decorator可以为您处理这些细节:

import functools 

def activate_security(func): 
    @functools.wraps(func) 
    def wrapper(self): 
     httpretty.enable() 
     uri = 'http://{}:{}/token'.format(HOST, PORT) 
     httpretty.register_uri(httpretty.GET, uri, 
           body=dumps({'token': 'dummy_token'}), 
           content_type='application/json') 
     func(self) 
     httpretty.disable() 
    return wrapper 
+0

非常感谢。特别是关于functools.wraps – cllamach 2014-12-01 18:59:34