Reputation: 10871
Sanity check please!
I'm trying to understand an unexpected test failure when including the exact message returned from an incorrect function call to the match
parameter of pytest.raises()
.
match – if specified, asserts that the exception matches a text or regex
The sequence of instructions in the repl below pretty much says it all, but for some reason the last test fails.
PS C:\Users\peter_000\OneDrive\git\test> pipenv run python
Loading .env environment variables…
Python 3.6.4 (v3.6.4:d48eceb, Dec 19 2017, 06:54:40) [MSC v.1900 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>>
>>>
>>>
>>> import pytest
>>> pytest.__version__
'4.4.1'
>>>
>>> with pytest.raises(TypeError, match='a string'):
... raise TypeError('a string') # passes
...
>>> def func():
... pass
...
>>> func(None)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: func() takes 0 positional arguments but 1 was given
>>>
>>>
>>> with pytest.raises(TypeError, match='func() takes 0 positional arguments but 1 was given'):
... func(None) # fails
...
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
TypeError: func() takes 0 positional arguments but 1 was given
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
File "C:\Users\peter_000\.virtualenvs\test-_0Fb_hDQ\lib\site-packages\_pytest\python_api.py", line 735, in __exit__
self.excinfo.match(self.match_expr)
File "C:\Users\peter_000\.virtualenvs\test-_0Fb_hDQ\lib\site-packages\_pytest\_code\code.py", line 575, in match
assert 0, "Pattern '{!s}' not found in '{!s}'".format(regexp, self.value)
AssertionError: Pattern 'func() takes 0 positional arguments but 1 was given' not found in 'func() takes 0 positional arguments but 1 was given'
>>>
I thought that perhaps the '()'
might mean something in regex that would cause the strings not to match but:
>>> with pytest.raises(TypeError, match='func()'):
... raise TypeError('func()')
... passes.
Upvotes: 22
Views: 16851
Reputation: 1596
Just do what the error message says and use re.escape()
:
with pytest.raises(TypeError, match=re.escape('func()')):
raise TypeError('func()')
Upvotes: 8
Reputation: 3155
I have a method that raises a ValueError
if too large of an integer is passed into the method. My test method looked like this:
import pytest
def test_func_exceeding_max_value():
with pytest.raises(ValueError, match="maximum value of (2**32)-1 exceeded"):
func(2**32)
This test is met with the error:
re.error: multiple repeat at position 20
Position 20 corresponds to this character:
maximum value of (2**32)-1 exceeded
^
As the error suggests, it is raised from an error in a regex
expression. What I failed to initially realize is that the match
parameter in pytest.raises
is a regular expression and not a literal string match. In the pytest
documentation, it states:
You can pass a match keyword parameter to the context-manager [pytest.raises] to test that a regular expression matches on the string representation of an exception...
You need to use two backslashes (\\
) instead of one before the special characters (
, *
, )
, etc. This is because when the match
parameter is sent to the re.match
method, the backslash itself needs to be escaped so that re.match
interprets that backslash as a literal character and will then escape the correct character:
def test_func_exceeding_max_value():
with pytest.raises(ValueError, match="maximum value of \\(2\\*\\*32\\)-1 exceeded"):
func(2**32)
Upvotes: 1
Reputation: 17761
Match takes a regular expression pattern, and some characters like ()
are special. You need to escape them:
>>> with pytest.raises(TypeError, match=r'func\(\) takes 0 positional arguments but 1 was given'):
... # ^ ^^^^
... func(None) # succeeds
>>>
The reason why it was failing before is that ()
in a regular expression corresponds to an empty group, and so your pattern would have matched the string func takes 0 positional arguments but 1 was given
.
The reason why match='func()'
passes is that the particular regex is looking for func
anywhere in the string: it may be followed or preceded by any text.
Upvotes: 26