Skip to content

Commit

Permalink
fix(verify): improve verify traceback and error messages (#66)
Browse files Browse the repository at this point in the history
- Ensure verify error message is helpful when actual call count matches the `times` argument
- Use `__tracebackhide__` to hide Decoy code from pytest traceback
  • Loading branch information
mcous authored Sep 21, 2021
1 parent 9473401 commit 9e74b36
Show file tree
Hide file tree
Showing 6 changed files with 41 additions and 12 deletions.
3 changes: 3 additions & 0 deletions decoy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
from .core import DecoyCore, StubCore
from .types import ClassT, FuncT, ReturnT

# ensure decoy does not pollute pytest tracebacks
__tracebackhide__ = True


class Decoy:
"""Decoy test double state container."""
Expand Down
3 changes: 3 additions & 0 deletions decoy/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
from .warning_checker import WarningChecker
from .types import ReturnT

# ensure decoy.core does not pollute Pytest tracebacks
__tracebackhide__ = True


class DecoyCore:
"""The DecoyCore class implements the main logic of Decoy."""
Expand Down
2 changes: 1 addition & 1 deletion decoy/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ def __init__(
heading=heading,
rehearsals=rehearsals,
calls=calls,
include_calls=times is None,
include_calls=times is None or times == len(calls),
)

super().__init__(message)
Expand Down
25 changes: 14 additions & 11 deletions decoy/stringify.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,21 +31,24 @@ def count(count: int, noun: str) -> str:
return f"{count} {noun}{'s' if count != 1 else ''}"


def join_lines(*lines: str) -> str:
"""Join a list of lines with newline characters."""
return os.linesep.join(lines).strip()


def stringify_error_message(
heading: str,
rehearsals: Sequence[BaseSpyRehearsal],
calls: Sequence[SpyCall],
include_calls: bool = True,
) -> str:
"""Stringify an error message about a rehearsals to calls comparison."""
return os.linesep.join(
[
heading,
stringify_call_list(rehearsals),
(
f"Found {count(len(calls), 'call')}"
f"{'.' if len(calls) == 0 or not include_calls else ':'}"
),
stringify_call_list(calls) if include_calls else "",
]
).strip()
return join_lines(
heading,
stringify_call_list(rehearsals),
(
f"Found {count(len(calls), 'call')}"
f"{'.' if len(calls) == 0 or not include_calls else ':'}"
),
stringify_call_list(calls) if include_calls else "",
)
3 changes: 3 additions & 0 deletions decoy/verifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
from .spy_calls import SpyCall, VerifyRehearsal, match_call
from .errors import VerifyError

# ensure decoy.verifier does not pollute Pytest tracebacks
__tracebackhide__ = True


class Verifier:
"""An interface to verify that spies were called as expected."""
Expand Down
17 changes: 17 additions & 0 deletions tests/test_errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,23 @@ class VerifyErrorSpec(NamedTuple):
]
),
),
VerifyErrorSpec(
rehearsals=[
VerifyRehearsal(spy_id=101, spy_name="spy_101", args=(1, 2, 3), kwargs={}),
],
calls=[
SpyCall(spy_id=101, spy_name="spy_101", args=(4, 5, 6), kwargs={}),
],
times=1,
expected_message=os.linesep.join(
[
"Expected exactly 1 call:",
"1.\tspy_101(1, 2, 3)",
"Found 1 call:",
"1.\tspy_101(4, 5, 6)",
]
),
),
]


Expand Down

0 comments on commit 9e74b36

Please sign in to comment.