Skip to content
Advertisement

How to pytest that a decorator is assigned to multiple functions

I read somewhere on SO that you should not test the decorator but the functionality of the wrapped function. Nevertheless there might be a shorter way of testing whether a certain decorator is assigned to multiple functions.

I have this decorator:

def check_user(func):
    """Only allow admins to change the user_id in the annotated function.

    Use as decorator: @check_user
    """

    @wraps(func)
    def wrapper(*args, **kwargs):
    ...

I have some tests to test the decorator function itself, e.g.:

def test_check_user(self):
    """Test the check user decorator if a non admin wants to overwrite the user."""
    with pytest.raises(ValueError) as ex:

        @check_user
        def foo(login: str, userId: str):
            return True

        foo(login="Foo", userId="Bar")

    assert ex.value.args[0] == "Only admin users may overwrite the userId."

Now I have about 20 FastAPI endpoints where I assigned this decorator. I want to avoid to repeat the same tests (see example above and other tests) for each function. So something like this would be great:

@pytest.mark.parametrize("method", ["foo", "bar", "gaga", ....])
def test_decorated_methods(self, method):
    assert assigned(check_user, method)  # or so

Advertisement

Answer

You should be able to parametrize test_check_user to check the same assert for every decorated function in one test. This is superior to just checking if a decorator is applied because it validates the actual functional requirement of preventing non-admins from changing userId.

Remember the goal of writing good tests is to protect you from your future self. While you currently feel you can infer a security feature from the presence of this decorator, can you be sure that this inference will always be true for the rest of the project’s lifetime? Better to make sure that your security features actually behave as intended.

@pytest.mark.parametrize("method,args,kwargs", [(my_func, ["Foo"], {}),
                                                (my_other_func, ["bar"], {})])
def test_cant_change_id_if_not_admin(func, args, kwargs):
    kwargs["userId"]="Bar"
    with pytest.raises(ValueError) as ex:
        func(*args, **kwargs)
    assert ex.value.args[0] == "Only admin users may overwrite the userId."
Advertisement