Skip to content

Commit

Permalink
Add order_matters argument to ShouldWarn context manager (#185)
Browse files Browse the repository at this point in the history
  • Loading branch information
stinovlas committed Sep 15, 2023
1 parent 544b436 commit 50f61f9
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 18 deletions.
20 changes: 16 additions & 4 deletions docs/warnings.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ causing the test in which it occurs to fail:
... warn("sorry dave, I can't let you do that")
Traceback (most recent call last):
...
AssertionError: sequence not as expected:
<BLANKLINE>
AssertionError:...
<SequenceComparison(ordered=True, partial=False)(failed)>
same:
[]
<BLANKLINE>
Expand All @@ -40,6 +40,7 @@ attributes differ:
<BLANKLINE>
actual:
[UserWarning("sorry dave, I can't let you do that"...)]
</SequenceComparison(ordered=True, partial=False)> (expected) != [UserWarning("sorry dave, I can't let you do that"...)] (actual)

You can check multiple warnings in a particular piece of code:

Expand All @@ -50,6 +51,16 @@ You can check multiple warnings in a particular piece of code:
... warn('you should fix that')
... warn('and that too')

If you don't care about the order of issued warnings, you can use ``order_matters=False``:

>>> from warnings import warn
>>> from testfixtures import ShouldWarn
>>> with ShouldWarn(UserWarning('you should fix that'),
... UserWarning('and that too'),
... order_matters=False):
... warn('and that too')
... warn('you should fix that')

If you want to inspect more details of the warnings issued, you can capture
them into a list as follows:

Expand Down Expand Up @@ -78,8 +89,8 @@ context it manages, it will raise an :class:`AssertionError` to indicate this:
... warn("woah dude")
Traceback (most recent call last):
...
AssertionError: sequence not as expected:
<BLANKLINE>
AssertionError:...
<SequenceComparison(ordered=True, partial=False)(failed)>
same:
[]
<BLANKLINE>
Expand All @@ -88,3 +99,4 @@ expected:
<BLANKLINE>
actual:
[UserWarning('woah dude'...)]
</SequenceComparison(ordered=True, partial=False)> (expected) != [UserWarning('woah dude'...)] (actual)
19 changes: 15 additions & 4 deletions testfixtures/shouldwarn.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import warnings
from typing import Union, Type

from testfixtures import Comparison as C, compare
from testfixtures import Comparison, SequenceComparison, compare


WarningOrType = Union[Warning, Type[Warning]]
Expand All @@ -26,6 +26,13 @@ class ShouldWarn(warnings.catch_warnings):
If no expected warnings are passed, you will need to inspect
the contents of the list returned by the context manager.
:param order_matters:
A keyword-only parameter that controls whether the order of the
captured entries is required to match those of the expected entries.
Defaults to ``True``.
:param filters:
If passed, these are used to create a filter such that only warnings you
are interested in will be considered by this :class:`ShouldWarn`
Expand All @@ -36,9 +43,10 @@ class ShouldWarn(warnings.catch_warnings):

_empty_okay = False

def __init__(self, *expected: WarningOrType, **filters):
def __init__(self, *expected: WarningOrType, order_matters: bool = True, **filters):
super(ShouldWarn, self).__init__(record=True)
self.expected = [C(e) for e in expected]
self.order_matters = order_matters
self.expected = [Comparison(e) for e in expected]
self.filters = filters

def __enter__(self):
Expand All @@ -52,7 +60,10 @@ def __exit__(self, exc_type, exc_val, exc_tb):
return
if not self.expected and self.recorded and not self._empty_okay:
return
compare(self.expected, actual=[wm.message for wm in self.recorded])
compare(
expected=SequenceComparison(*self.expected, ordered=self.order_matters),
actual=[wm.message for wm in self.recorded]
)


class ShouldNotWarn(ShouldWarn):
Expand Down
51 changes: 41 additions & 10 deletions testfixtures/tests/test_shouldwarn.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,12 @@ def test_warn_expected(self):

def test_warn_not_expected(self):
with ShouldAssert(
"sequence not as expected:\n\n"
"\n<SequenceComparison(ordered=True, partial=False)(failed)>\n"
"same:\n[]\n\n"
"expected:\n[]\n\n"
"actual:\n[UserWarning('foo'"+comma+")]"
"actual:\n[UserWarning('foo'"+comma+")]\n"
"</SequenceComparison(ordered=True, partial=False)> (expected) "
"!= [UserWarning('foo'"+comma+")] (actual)"
):
with warnings.catch_warnings(record=True) as backstop:
with ShouldNotWarn():
Expand All @@ -41,10 +43,11 @@ def test_no_warn_expected(self):

def test_no_warn_not_expected(self):
with ShouldAssert(
"sequence not as expected:\n\n"
"\n<SequenceComparison(ordered=True, partial=False)(failed)>\n"
"same:\n[]\n\n"
"expected:\n[<C:builtins.UserWarning>args: ('foo',)</>]"
"\n\nactual:\n[]"
"\n\nactual:\n[]\n"
"</SequenceComparison(ordered=True, partial=False)> (expected) != [] (actual)"
):
with ShouldWarn(UserWarning('foo')):
pass
Expand All @@ -64,17 +67,42 @@ def test_multiple_warnings(self):
self.assertTrue('foo' in content)
self.assertTrue('bar' in content)

def test_multiple_warnings_ordered(self):
with warnings.catch_warnings(record=True) as backstop:
with ShouldWarn(UserWarning('foo'), UserWarning('bar')):
warnings.warn('foo')
warnings.warn('bar')
compare(len(backstop), expected=0)

def test_multiple_warnings_wrong_order(self):
with ShouldRaise(AssertionError) as s:
with ShouldWarn(UserWarning('foo'), UserWarning('bar')):
warnings.warn('bar')
warnings.warn('foo')
content = str(s.raised)
self.assertTrue('foo' in content)
self.assertTrue('bar' in content)

def test_multiple_warnings_ignore_order(self):
with warnings.catch_warnings(record=True) as backstop:
with ShouldWarn(UserWarning('foo'), UserWarning('bar'), order_matters=False):
warnings.warn('bar')
warnings.warn('foo')
compare(len(backstop), expected=0)

def test_minimal_ok(self):
with ShouldWarn(UserWarning):
warnings.warn('foo')

def test_minimal_bad(self):
with ShouldAssert(
"sequence not as expected:\n\n"
"\n<SequenceComparison(ordered=True, partial=False)(failed)>\n"
"same:\n[]\n\n"
"expected:\n"
"[<C:builtins.DeprecationWarning(failed)>wrong type</>]\n\n"
"actual:\n[UserWarning('foo'"+comma+")]"
"actual:\n[UserWarning('foo'"+comma+")]\n"
"</SequenceComparison(ordered=True, partial=False)> (expected) "
"!= [UserWarning('foo'"+comma+")] (actual)"
):
with ShouldWarn(DeprecationWarning):
warnings.warn('foo')
Expand All @@ -87,14 +115,16 @@ def test_maximal_ok(self):

def test_maximal_bad(self):
with ShouldAssert(
"sequence not as expected:\n\n"
"\n<SequenceComparison(ordered=True, partial=False)(failed)>\n"
"same:\n[]\n\n"
"expected:\n[\n"
"<C:builtins.DeprecationWarning(failed)>\n"
"attributes differ:\n"
"'args': ('bar',) (Comparison) != ('foo',) (actual)\n"
"</C:builtins.DeprecationWarning>]\n\n"
"actual:\n[DeprecationWarning('foo'"+comma+")]"
"actual:\n[DeprecationWarning('foo'"+comma+")]\n"
"</SequenceComparison(ordered=True, partial=False)> (expected) "
"!= [DeprecationWarning('foo'"+comma+")] (actual)"
):
with ShouldWarn(DeprecationWarning('bar')):
warnings.warn_explicit(
Expand Down Expand Up @@ -130,10 +160,11 @@ def test_filter_present(self):

def test_filter_missing(self):
with ShouldAssert(
"sequence not as expected:\n\n"
"\n<SequenceComparison(ordered=True, partial=False)(failed)>\n"
"same:\n[]\n\n"
"expected:\n[<C:builtins.DeprecationWarning>]\n\n"
"actual:\n[]"
"actual:\n[]\n"
"</SequenceComparison(ordered=True, partial=False)> (expected) != [] (actual)"
):
with ShouldWarn(DeprecationWarning,
message="This function is deprecated."):
Expand Down

0 comments on commit 50f61f9

Please sign in to comment.