Introduce no_fnmatch_line/no_re_match_line in pytester
The current idiom is to use: assert re.match(pat, result.stdout.str()) Or assert line in result.stdout.str() But this does not really give good results when it fails. Those new functions produce similar output to ther other match lines functions.
This commit is contained in:
parent
b847d5712b
commit
0c18e24433
|
@ -0,0 +1,19 @@
|
|||
``pytester`` learned two new functions, `no_fnmatch_line <https://docs.pytest.org/en/latest/reference.html#_pytest.pytester.LineMatcher.no_fnmatch_line>`_ and
|
||||
`no_re_match_line <https://docs.pytest.org/en/latest/reference.html#_pytest.pytester.LineMatcher.no_re_match_line>`_.
|
||||
|
||||
The functions are used to ensure the captured text *does not* match the given
|
||||
pattern.
|
||||
|
||||
The previous idiom was to use ``re.match``:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
assert re.match(pat, result.stdout.str()) is None
|
||||
|
||||
Or the ``in`` operator:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
assert text in result.stdout.str()
|
||||
|
||||
But the new functions produce best output on failure.
|
|
@ -1318,8 +1318,7 @@ class LineMatcher:
|
|||
|
||||
The argument is a list of lines which have to match and can use glob
|
||||
wildcards. If they do not match a pytest.fail() is called. The
|
||||
matches and non-matches are also printed on stdout.
|
||||
|
||||
matches and non-matches are also shown as part of the error message.
|
||||
"""
|
||||
__tracebackhide__ = True
|
||||
self._match_lines(lines2, fnmatch, "fnmatch")
|
||||
|
@ -1330,8 +1329,7 @@ class LineMatcher:
|
|||
The argument is a list of lines which have to match using ``re.match``.
|
||||
If they do not match a pytest.fail() is called.
|
||||
|
||||
The matches and non-matches are also printed on stdout.
|
||||
|
||||
The matches and non-matches are also shown as part of the error message.
|
||||
"""
|
||||
__tracebackhide__ = True
|
||||
self._match_lines(lines2, lambda name, pat: re.match(pat, name), "re.match")
|
||||
|
@ -1374,3 +1372,40 @@ class LineMatcher:
|
|||
else:
|
||||
self._log("remains unmatched: {!r}".format(line))
|
||||
pytest.fail(self._log_text)
|
||||
|
||||
def no_fnmatch_line(self, pat):
|
||||
"""Ensure captured lines do not match the given pattern, using ``fnmatch.fnmatch``.
|
||||
|
||||
:param str pat: the pattern to match lines.
|
||||
"""
|
||||
__tracebackhide__ = True
|
||||
self._no_match_line(pat, fnmatch, "fnmatch")
|
||||
|
||||
def no_re_match_line(self, pat):
|
||||
"""Ensure captured lines do not match the given pattern, using ``re.match``.
|
||||
|
||||
:param str pat: the regular expression to match lines.
|
||||
"""
|
||||
__tracebackhide__ = True
|
||||
self._no_match_line(pat, lambda name, pat: re.match(pat, name), "re.match")
|
||||
|
||||
def _no_match_line(self, pat, match_func, match_nickname):
|
||||
"""Ensure captured lines does not have a the given pattern, using ``fnmatch.fnmatch``
|
||||
|
||||
:param str pat: the pattern to match lines
|
||||
"""
|
||||
__tracebackhide__ = True
|
||||
nomatch_printed = False
|
||||
try:
|
||||
for line in self.lines:
|
||||
if match_func(line, pat):
|
||||
self._log("%s:" % match_nickname, repr(pat))
|
||||
self._log(" with:", repr(line))
|
||||
pytest.fail(self._log_text)
|
||||
else:
|
||||
if not nomatch_printed:
|
||||
self._log("nomatch:", repr(pat))
|
||||
nomatch_printed = True
|
||||
self._log(" and:", repr(line))
|
||||
finally:
|
||||
self._log_output = []
|
||||
|
|
|
@ -457,6 +457,52 @@ def test_linematcher_with_nonlist():
|
|||
assert lm._getlines(set()) == set()
|
||||
|
||||
|
||||
@pytest.mark.parametrize("function", ["no_fnmatch_line", "no_re_match_line"])
|
||||
def test_no_matching(function):
|
||||
""""""
|
||||
if function == "no_fnmatch_line":
|
||||
match_func_name = "fnmatch"
|
||||
good_pattern = "*.py OK*"
|
||||
bad_pattern = "*X.py OK*"
|
||||
else:
|
||||
assert function == "no_re_match_line"
|
||||
match_func_name = "re.match"
|
||||
good_pattern = r".*py OK"
|
||||
bad_pattern = r".*Xpy OK"
|
||||
|
||||
lm = LineMatcher(
|
||||
[
|
||||
"cachedir: .pytest_cache",
|
||||
"collecting ... collected 1 item",
|
||||
"",
|
||||
"show_fixtures_per_test.py OK",
|
||||
"=== elapsed 1s ===",
|
||||
]
|
||||
)
|
||||
|
||||
def check_failure_lines(lines):
|
||||
expected = [
|
||||
"nomatch: '{}'".format(good_pattern),
|
||||
" and: 'cachedir: .pytest_cache'",
|
||||
" and: 'collecting ... collected 1 item'",
|
||||
" and: ''",
|
||||
"{}: '{}'".format(match_func_name, good_pattern),
|
||||
" with: 'show_fixtures_per_test.py OK'",
|
||||
]
|
||||
assert lines == expected
|
||||
|
||||
# check the function twice to ensure we don't accumulate the internal buffer
|
||||
for i in range(2):
|
||||
with pytest.raises(pytest.fail.Exception) as e:
|
||||
func = getattr(lm, function)
|
||||
func(good_pattern)
|
||||
obtained = str(e.value).splitlines()
|
||||
check_failure_lines(obtained)
|
||||
|
||||
func = getattr(lm, function)
|
||||
func(bad_pattern) # bad pattern does not match any line: passes
|
||||
|
||||
|
||||
def test_pytester_addopts(request, monkeypatch):
|
||||
monkeypatch.setenv("PYTEST_ADDOPTS", "--orig-unused")
|
||||
|
||||
|
|
Loading…
Reference in New Issue