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 #215
  • Loading branch information
webknjaz committed Dec 11, 2020
1 parent 9e1464b commit 4e1e906
Show file tree
Hide file tree
Showing 2 changed files with 20 additions and 6 deletions.
4 changes: 2 additions & 2 deletions src/pytest_mock/plugin.py
Original file line number Diff line number Diff line change
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
22 changes: 18 additions & 4 deletions tests/test_pytest_mock.py
Original file line number Diff line number Diff line change
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,31 @@ 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 4e1e906

Please sign in to comment.