Jianyuan Ma
Jianyuan Ma

Reputation: 325

Use PyTest to test the decorated exception handler is called

I use the decorator to handle the exception in Python. Here is the decorator function:

def docker_exception_handler(func):
    def wrapper(*args, **kwargs):
        logger = logging.getLogger('docker_exception')
        try:
           func(*args, **kwargs)
        except SubProcessException:
           logger.critical(
               f'Failed to pull {args[1]} in function {func.__name__}\n')

    return wrapper

Now I would like to use Pytest when the SubProcessException raised this function is called. Something like:

@docker_exception_handler
def trigger_docker_error(class_name, docker_image):
    raise PullDockerImageError

def test_docker_error():
    with patch.object(customized_exceptions,
                  'docker_exception_handler') as mock:
         trigger_docker_error("test", "test_docker_image")
    mock.assert_called_once()

But the mock did not get the call, failed with message AssertionError: Expected 'docker_exception_handler' to have been called once. Called 0 times I do not know why.

Upvotes: 1

Views: 661

Answers (1)

NobbyNobbs
NobbyNobbs

Reputation: 1434

Decorators applied in import time, so there is not much sense to mock them and asserting something in runtime.

In your case you could mock the logger and check if it was called with right arguments. Here I'm using mocker fixture from pytest-mock

import functools
import logging

def exception_handler(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        logger = logging.getLogger('docker_exception')
        try:
           func(*args, **kwargs)
        except Exception:
           logger.critical(
               f'Failed to pull {args[1]} in function {func.__name__}\n')
    return wrapper

@exception_handler
def trigger_error(class_name, docker_image):
    raise TypeError()

def test_error(mocker):
    logger = logging.getLogger('docker_exception')
    mock = mocker.patch.object(logger, 'critical')

    trigger_error("test", "test_docker_image")

    mock.assert_called_once()
    mock.assert_called_with(f'Failed to pull test_docker_image in function trigger_error\n')

if __name__ == "__main__":
    import pytest
    pytest.main([__file__])

Upvotes: 2

Related Questions