1

I have written a decorator that is working correctly but i stumbled with the correct solution by trial and error and my litle knowledge about decorators tells me that something is not well defined.

The case is i'm mocking a Rest Api to do some TDD, and this Rest is behind a token security. So before making any request i first must get my user token. I'm using httpretty for mocking the API.

So far i had to register_uri in every test case, one to mock the /token resource and another to test any other resource. But i found that very cumbersome, so a came with a solution to write a simple decorator that would mock the /token and then only had to mock the tested resource.

This is my currently working decorator...

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

And this is how is called.

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

I had to pass the test_case parameter to the inner function 'cause without it it wouldn't work, and that test_case is test_case_one method, which i thought it would be passed in the func argument, but func in the outer scope holds the object at memory of test_case.

Should't be func the returned value of a decorator? If i do that, the decorator doesn't work. When the inner function is passed that parameter?

4

1 回答 1

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; 在你的包装器中,你可以通过简单地传递给它来调用unbound 。 func()self

出于调试目的,将一些函数属性从包装函数复制到包装器通常会更好;@functools.wraps()装饰者可以为您处理这些细节:

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
于 2014-12-01T16:47:42.593 回答