pytest: Confusion between message and match parameters in pytest.raises
After analyzing several code bases, it appears there is frequent confusion by users between the match
and message
parameters in pytest.raises
(and consequentially pytest.warns
).
In most cases, users probably would want match
but use message
by accident, because is it visually similar and the naming does not differentiate their functionality enough. As a result the written test produces false positive which is quite dangerous.
For instance, here are instances of such errors in popular open-source projects,
- https://github.com/pandas-dev/pandas/pull/22681#discussion_r217181448
- https://github.com/scipy/scipy/pull/9264
- https://github.com/scikit-learn/scikit-learn/pull/12001
the fact that these cases ended up in the code despite fairly thorough review is telling.
There was also an earlier issue illustrating this confusion in https://github.com/pytest-dev/pytest/issues/3350
Searching on github for "pytest.raises(ValueError, message="
for instance, also shows a number of examples that meant to use "match"
instead of "message"
(though sometimes it’s hard to tell).
One possibility could be to deprecate the message
parameter and rename it to something more explicit. I saw there were some deprecations related to the use of node.warn
in 3.8, not sure if that’s related. However, following the same logic even if pytest.raises(ValueError('expected message'))
worked it would still not address all the existing code that currently uses the message
parameter incorrectly.
EDIT by @nicoddemus
The message
parameter has been deprecated and will be removed in pytest 5.0, please see the docs for an alternative which provides the same functionality:
https://docs.pytest.org/en/latest/deprecations.html#raises-message-deprecated
About this issue
- Original URL
- State: closed
- Created 6 years ago
- Reactions: 3
- Comments: 55 (38 by maintainers)
Commits related to this issue
- Deprecate the 'message' parameter of pytest.raises Fix #3974 — committed to nicoddemus/pytest by nicoddemus 6 years ago
- Deprecate the 'message' parameter of pytest.raises Fix #3974 — committed to nicoddemus/pytest by nicoddemus 6 years ago
- Deprecate the 'message' parameter of pytest.raises Fix #3974 — committed to nicoddemus/pytest by nicoddemus 6 years ago
- Fix incorrect use of pytest.raises(..., message=...) See https://github.com/pytest-dev/pytest/issues/3974. — committed to mgedmin/eazysvn by mgedmin 5 years ago
- Use 'match' instead of 'message' with pytest.raises() Since the former is actually what we should have been using all along. See: https://github.com/pytest-dev/pytest/issues/3974 — committed to mozilla/treeherder by edmorley 5 years ago
- Update pytest to 4.1.0 (#4427) And use 'match' instead of 'message' with pytest.raises(), since the former is actually what we should have been using all along. See: https://github.com/pytest-de... — committed to mozilla/treeherder by pyup-bot 5 years ago
- Merge #1680 1680: Scheduled weekly dependency update for week 01 r=mythmon a=pyup-bot ### Update [botocore](https://pypi.org/project/botocore) from **1.12.62** to **1.12.74**. <details> <su... — committed to mozilla/normandy by bors[bot] 5 years ago
- Merge #1680 1680: Scheduled weekly dependency update for week 01 r=mythmon a=pyup-bot ### Update [botocore](https://pypi.org/project/botocore) from **1.12.62** to **1.12.74**. <details> <su... — committed to mozilla/normandy by bors[bot] 5 years ago
- Merge #1680 1680: Scheduled weekly dependency update for week 01 r=mythmon a=pyup-bot ### Update [botocore](https://pypi.org/project/botocore) from **1.12.62** to **1.12.74**. <details> <su... — committed to mozilla/normandy by bors[bot] 5 years ago
- Merge #54 54: Update pytest to 4.1.1 r=rehandalal a=pyup-bot This PR updates [pytest](https://pypi.org/project/pytest) from **4.0.2** to **4.1.1**. <details> <summary>Changelog</summary> ... — committed to rehandalal/therapist by bors[bot] 5 years ago
- replace message with match in pytest.raises see https://github.com/pytest-dev/pytest/issues/3974 — committed to StanczakDominik/PlasmaPy by StanczakDominik 5 years ago
- Update dependecies to latest version Tests were updated to avoid a depreciated warning in pytest, see pytest-dev/pytest#3974 for details. — committed to nqminds/nqm-iot-database-py by aloisklink 5 years ago
- Improve pytest.raises 'message' deprecation docs Based on recent discussions in #3974 — committed to nicoddemus/pytest by nicoddemus 5 years ago
- [tests] Replace 'message' in pytest.raises with 'match' There's a common confusion between both, see pytest-dev/pytest#3974 The patterns for match needed some adjustments due to the changes made to ... — committed to michael-k/aws-cfn-template-flip by michael-k 5 years ago
- Remove 'message' parameter docs from assert.rst As per: https://github.com/pytest-dev/pytest/issues/3974#issuecomment-463462732 Also made the 'match' parameter more prominent — committed to nicoddemus/pytest by nicoddemus 5 years ago
- Fix Pytest warning for pytest.raises change Fix these warnings seen on a test run since upgrading Pytest: ``` tests/testapp/test_cache.py::MySQLCacheTests::test_get_or_set_version /Users/chainz/Do... — committed to adamchainz/django-mysql by adamchainz 5 years ago
- Fix Pytest warning for pytest.raises change Fix these warnings seen on a test run since upgrading Pytest: ``` tests/testapp/test_cache.py::MySQLCacheTests::test_get_or_set_version /Users/chainz/Do... — committed to adamchainz/django-mysql by adamchainz 5 years ago
- Fix Pytest warning for pytest.raises change (#532) Fix these warnings seen on a test run since upgrading Pytest: ``` tests/testapp/test_cache.py::MySQLCacheTests::test_get_or_set_version /User... — committed to adamchainz/django-mysql by adamchainz 5 years ago
- Test for message in raised exception in test_water_supply Message parameter is deprecated in pytest.raises, see https://github.com/pytest-dev/pytest/issues/3974 for discussion. — committed to tomalrussell/smif by tomalrussell 5 years ago
- Fix pytest.raises() deprecation warning Please comment on https://github.com/pytest-dev/pytest/issues/3974 if you have concerns about removal of this parameter. — committed to eirnym/cerberus by eirnym 5 years ago
I would like to cast my vote in favour of
message_when_not_raised
.I think it’s useful to be able to provide a message for failed assertions. That’s why the assert keyword has that option, right? I’m honestly surprised I seem to be the minority.
And this
…seems more intuitive than…
…to me.
I understand that the original name might’ve been confusing and should be changed. But I did use
message
for the original purpose and am sad to see it gone.@pawelswiecki thanks for the feedback!
You can get the same functionality by calling
pytest.fail
right after the code which is supposed to raise the exception, like this:This way you will still get the exception message if the exception is not raised.
Hi @rth thanks for the excellent description and examples, they are extremely helpful in making the case about doing something about this matter.
So we have two proposals:
Deprecate
message
and use another name. I think using a longer name likeshow_failure_message
is perfectly fine, my gut feeling is that this parameter is rarely used (only misused). This option to me is safest.Removing the parameter altogether (after deprecating it of course). This would make it match (heh)
pytest.warn
signature as well. This is an excellent option in the long term if there are no good use-cases for it.I’m writing an e-mail to the mailing list to ask opinions to this, in particular if there are good cases for
message
/show_failure_message
that I’m not visualizing.after checking we should deprecate the
message
name and rename it to something likefailure_message
I’ve just encountered this in a test that was using it correctly. I got confused because I, first changed
message
tomatch
but it was being used correctly (that it was used correctly in our code wasn’t entirely clear from the code context). Then I looked at the documentation to find whatmessage
was supposed to do and found it had been scrubbed completely.Then came here and it’s only after scanning through this whole thread that I see a replacement solution that doesn’t seem very satisfactory:
Because - well, because it feels like I’m manually doing pytest’s job; working around something that pytest should do. It feels like rather than encoding the test expectation in two places and requires thinking about whether the tests are competing. I’d be better off writing the more familiar:
which somewhat defeats the point of
raises
in the first place.Perhaps in the future, I feel that it would have been better to have a solution or at least suggestion in place before telling everyone not to do what they’ve been told to do until then.
Guess we’ll have to agree to disagree
I don’t dispute that there were a lot of mistaken usages.
But it’s not about the ratio of correct and in correct, because supporting one does not rule out the other: both groups could get what they want without bothering each other, by just changing the name.
And I support the removing of api pitfalls like this. But my point is that if the argument has a clear name, there is no pitfall.
It’s hard to argue about readabilty because it’s subjective, but I personally can’t agree that throwing inside the with block is more clear than a properly named parameter.
On Mon, May 6, 2019, 22:41 Anthony Sottile notifications@github.com wrote:
@manoadamro and @fmigneault,
So far we have considered
message
very error prone, regardless if the docs state otherwise: if an API consistently trips the users, it is not a good API, regardless of the docs.Having said that, please consider that we have a very easy alternative without relying on the message parameter. Using @manoadamro’s example:
To get the exact same functionality all you need to do is change it to:
IMHO this seems like a good solution while avoiding the pitfalls that the
message
parameter brings.Yes. The default “DID NOT RAISE: TimeoutError” is not very helpful as a failure message. (Obviously, having to type three lines more is not the end of the world.)
Don’t know if it’s something you generally try not to do but since people are sent to this ticket by the message (and update lag times) I wonder if it might be worth editing the first post to put a link to the deprecation workaround page at the top.
I would agree if we were talking about something like 50% of users use the parameter, the other 50% don’t. But our take from this thread was that it was used very little, so we would rather not keeping it in the API at all.
Our take comes from the fact that this has been issuing a warning since 4.1, stating that people could comment in this thread if they felt the parameter was useful; the few people who ended up commenting did use the alternative method or even stopped using the
message
parameter at all because it didn’t really help in their case. At least this is my personal impression.I feel like, if the name of the argument was the problem, the obvious solution is renaming it. It seems there were multiple people using this and wanting to continue using this. Not really sure how pytest improves by insisting on workarounds.
While reading our Backward Compatibility Policy:
I realized that option 2) can use a warning like this:
The feature keeps working, people can chime in with valid use cases, and we don’t introduce a new parameter to keep around forever. Hmm.
Great idea, done. 👍
Actually we already have that documented in the “deprecation” docs, but I have added links to it in the main documentation (https://github.com/pytest-dev/pytest/pull/5218). Thanks!
So basically, because people cannot read docs, you penalize users that where using
message
correctly? 😕 I would also like to have at least a replacement keyword to keep functionality. Sometime the reason as to why the raised exception is expected is not as obvious for other coders…Maybe use
reason
as it seems to be used forskip
markers? Otherwise, +1 ifmessage_when_not_raised
is the preferred way to go.@nicoddemus Right, thanks for the tip!
I used
message
parameter together withmark.parametrize
in testing validation. It was used to state what exactly is being tested in the current parameter set. Usingmessage
parameter was 50% for the user running tests and 50% for documenting purposes. Now, to avoid the warning, I’m forced to change it to a message in comments, which is not that bad (I still have 50% functionality left), yet it’s annoying.Thanks @tomaskrizek for the feedback.
I think the alternative proposed by @asottile is on point:
I’m still not convinced – I think the stacktrace should already have sufficient information that you don’t need the
message
parameter – it shows exactly the line of code you’re looking at and the test name. And actually, I think themessage=
parameter makes the output worse.In the example you get a stacktrace (doctored because I don’t have your code):
That is, I’d much rather see and debug that output than your current failure scenario (again doctored):
(note that the doctoring was just to put your code example in and I didn’t change any of the pytest-parts)
If you want the exact behaviour you had before, I’d suggest this instead:
Was:
and is now back to the more explicit:
…which has the benefit of also printing the message if the test fails, so is all around better I guess 😉
+1 on changing this argument BTW, I’ve fallen into that trap as well.