Skip to content

Commit

Permalink
Ensure all the exceptions cat be spied on
Browse files Browse the repository at this point in the history
This change replaces `Exception` with `BaseException` in the `spy()`
method provided by the `mocker` fixture. This enables the caller to
spy on things like `KeyboardInterrupt`, `GeneratorExit` and
`SystemExit` exceptions that hasn't been possible before because of
a bug.

Before this change, any occurances of the above exceptions caused
`spy()` to assign `None` to both `spy_return` and `spy_exception`
attributes.

Fixes pytest-dev#215
  • Loading branch information
webknjaz committed Dec 11, 2020
1 parent 9e1464b commit 73d9fc0
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 6 deletions.
4 changes: 2 additions & 2 deletions src/pytest_mock/plugin.py
Expand Up @@ -114,7 +114,7 @@ def wrapper(*args, **kwargs):
spy_obj.spy_exception = None
try:
r = method(*args, **kwargs)
except Exception as e:
except BaseException as e:
spy_obj.spy_exception = e
raise
else:
Expand All @@ -126,7 +126,7 @@ async def async_wrapper(*args, **kwargs):
spy_obj.spy_exception = None
try:
r = await method(*args, **kwargs)
except Exception as e:
except BaseException as e:
spy_obj.spy_exception = e
raise
else:
Expand Down
23 changes: 19 additions & 4 deletions tests/test_pytest_mock.py
Expand Up @@ -2,7 +2,7 @@
import platform
import sys
from contextlib import contextmanager
from typing import Callable, Any, Tuple, Generator
from typing import Callable, Any, Tuple, Generator, Type
from unittest.mock import MagicMock

import pytest
Expand Down Expand Up @@ -235,17 +235,32 @@ def bar(self, arg):
assert spy.spy_return == 20


def test_instance_method_spy_exception(mocker: MockerFixture) -> None:
# Ref: https://docs.python.org/3/library/exceptions.html#exception-hierarchy
@pytest.mark.parametrize(
'exc_cls',
(
BaseException,
Exception,
GeneratorExit, # BaseException
KeyboardInterrupt, # BaseException
RuntimeError, # regular Exception
SystemExit, # BaseException
)
)
def test_instance_method_spy_exception(
exc_cls: Type[BaseException],
mocker: MockerFixture,
) -> None:
class Foo:
def bar(self, arg):
raise Exception("Error with {}".format(arg))
raise exc_cls("Error with {}".format(arg))

foo = Foo()
spy = mocker.spy(foo, "bar")

expected_calls = []
for i, v in enumerate([10, 20]):
with pytest.raises(Exception, match="Error with {}".format(v)) as exc_info:
with pytest.raises(exc_cls, match="Error with {}".format(v)) as exc_info:
foo.bar(arg=v)

expected_calls.append(mocker.call(arg=v))
Expand Down

0 comments on commit 73d9fc0

Please sign in to comment.