How to assert that a method is decorated with python unittest?
Asked Answered
A

2

11

I have a decorator and I want to assert that certain methods in my code are decorated with it.

import functools

def decorator(func):
    def _check_something(*args, **kwargs):
        # some logic in here
        return func(*args, **kwargs)
    return functools.wraps(func)(_check_something)

class MyClass(object):

    @decorator
    def my_method(foo, bar):
        pass

How do I assert with unittest (unitttest2) that my_method has @decorator and no-one removed it, and it was not forgotten?

Aboriginal answered 4/4, 2012 at 13:30 Comment(9)
Do some checks that test if the function (it's not called a "method" in Python, btw) has the correct behaviour, raising the correct exceptions etc. If it does, everything is fine.Friedly
updated the example to have a real method instead of just a dangling module function.Aboriginal
With unit tests, you only test if the function does the right thing. The whole point of this is that people can refactor the actual implementation as they want, as long as they don't break the functionality. What you're trying to do has nothing to do with unit testing.Nich
@NiklasB.: Not necessarily, but in this case I wouldn't go that route.Friedly
@SvenMarnach : To my knowledge, functions in classes are called methods as well in Python ( see e.g., docs.python.org/tutorial/classes.html )Yoruba
@SimeonVisser: In the first version of the question, it has been a module-level function.Friedly
@SvenMarnach: Ah indeed, I see it in the revision history.Yoruba
Possible duplicate of Check if a function has a decoratorTherese
Possible duplicate of Introspection to get decorator names on a methodTherese
C
3

You can do that by relying on your decorator to mark the wrapper function with an attribute, that you then assert.

A good practice is to have the decorator set a __wrapped__ attribute pointing to the original function on the returned wrapper.

thus:

def decorator(func):
    @functools.wraps(func)
    def _check_something(*args, **kwargs):
        # some logic in here
        return func(*args, **kwargs)
    _check_something.__wrapped__ = func   # <== add this
    return _check_something

and then, on your test code:

assert getattr(MyClass.my_method, "__wrapped__").__name__ == 'my_method'
Cameleer answered 4/4, 2012 at 13:54 Comment(3)
The attribute holding the wrapped function should be called __wrapped__.Friedly
Evggeny: did you test the edits you made to my answer? retriveng hte method weithout the class __dict__, in Python 2.x, gives you a bound method - no the function object - I don't think the bound method copies the __wrapped__ attribute of the underlying function. Oh - I tested it now, indeed, the method object proxies the accesses tothe function object attributes - I did not know that.Cameleer
This would check for the presence of some decorator, but not necessarily the specific decorator function. Is there a way of testing the presence of a specific decorator?Banka
L
5

If for some reason you can't modify the decorator, you could also try checking for some characteristic of a closed variable.

In your example, you know that the original my_method is the only variable closed by the decorator, so you could:

assert (my_method.__closure__ and 
           my_method.__closure__[0].cell_contents.__name__ == my_method.__name__)
Lederer answered 4/4, 2012 at 14:1 Comment(0)
C
3

You can do that by relying on your decorator to mark the wrapper function with an attribute, that you then assert.

A good practice is to have the decorator set a __wrapped__ attribute pointing to the original function on the returned wrapper.

thus:

def decorator(func):
    @functools.wraps(func)
    def _check_something(*args, **kwargs):
        # some logic in here
        return func(*args, **kwargs)
    _check_something.__wrapped__ = func   # <== add this
    return _check_something

and then, on your test code:

assert getattr(MyClass.my_method, "__wrapped__").__name__ == 'my_method'
Cameleer answered 4/4, 2012 at 13:54 Comment(3)
The attribute holding the wrapped function should be called __wrapped__.Friedly
Evggeny: did you test the edits you made to my answer? retriveng hte method weithout the class __dict__, in Python 2.x, gives you a bound method - no the function object - I don't think the bound method copies the __wrapped__ attribute of the underlying function. Oh - I tested it now, indeed, the method object proxies the accesses tothe function object attributes - I did not know that.Cameleer
This would check for the presence of some decorator, but not necessarily the specific decorator function. Is there a way of testing the presence of a specific decorator?Banka

© 2022 - 2024 — McMap. All rights reserved.