-
-
Notifications
You must be signed in to change notification settings - Fork 30.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
TestCase.assertRaisesString #123013
Comments
I am a bit sceptical about adding features that only take 3 lines of code to implement in your own code (from your example): def assertRaisesString(self, exc, string, *args, **kwargs):
escaped = re.escape(string)
return self.assertRaisesRegex(exc, f'^{escaped}$', *args, **kwds) On the other hand, in our own test code this is a very popular pattern. I would vote for direct string compare, instead of escaped regex match (if this feature is accepted). I think that adding performance numbers here can help: how fast it is to run a test case with |
I'm also of the same mind. What I could however suggest is that we extend the |
To me it probably would be most natural to have only single method However, there is an issue of callback with full I used to use similar patterns in the past, but don't do this anymore, instead just have |
There are many more required unittest features on the list. I am skeptical about this one. |
Benchmark results (100_000 runs): regex-convenient: 0.4 s Relative difference is non-trivial, however aggregate impact is small. Could have a slight impact far far in the future, but now it is tiny. CPython - 2041 hits of I am +1 for convenience. I think it is useful to have this for CPython tests. And +0.1 on implementation. Alternatively, could just add 3 lines of Python, but I am still slightly biased towards not computing more than needed given it is a simple addition. import re, time
import unittest as utt
RNG = list(range(100_000))
class TestCase(utt.TestCase):
def bench_string(self):
for i in RNG:
with self.assertRaisesString(TypeError, 'Error Message'):
raise TypeError('Error Message')
def bench_regex_manual(self):
for i in RNG:
with self.assertRaisesRegex(TypeError, '^Error Message$'):
raise TypeError('Error Message')
def bench_regex_convenient(self):
for i in RNG:
msg = 'Error Message'
msg = re.escape(msg)
with self.assertRaisesRegex(TypeError, f'^{msg}$'):
raise TypeError('Error Message')
def test_bench(self):
for f in [self.bench_regex_convenient, self.bench_regex_manual, self.bench_string]:
t = time.perf_counter()
f()
print(f'{(time.perf_counter() - t) * 1000:.2f} ms')
if __name__ == '__main__':
utt.main() |
Usually, it is enough to only test that some words or sequence of words are occurred in the error message, just to differentiate it from other errors with the same type. Such tests are less fragile, you can slightly rewrite the error message without changing tests. If you need to check the exact error message, it may be better to write it as: with self.assertRaises(...) as cm:
...
self.assertEqual(str(cm.exception), ...) This works also for warnings, and you can test also other attributes, for example the stack level for warnings. |
This is sensible. As long as it is clear that this is the recommended pattern for it, I think this is sufficient. If everyone is happy with this, I am closing this issue and PR. |
. |
Feature or enhancement
Proposal:
More often I need to check for exact error message and do not need regular expression.
For pure convenience, this would look like.
However, as using regular expression is overkill, could just add pure string case to
_AssertRaisesBaseContext
:Has this already been discussed elsewhere?
This is a minor feature, which does not need previous discussion elsewhere
Links to previous discussion of this feature:
No response
Linked PRs
The text was updated successfully, but these errors were encountered: