Skip to content
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

assertRaises(Regex)?(Exception) is problematic #106300

Closed
sobolevn opened this issue Jul 1, 2023 · 5 comments
Closed

assertRaises(Regex)?(Exception) is problematic #106300

sobolevn opened this issue Jul 1, 2023 · 5 comments
Assignees
Labels
tests Tests in the Lib/test dir type-bug An unexpected behavior, bug, or error

Comments

@sobolevn
Copy link
Member

sobolevn commented Jul 1, 2023

assertRaises(Exception) is problematic, because it does not really test anything.
For example, here's the test code from test_mailbox.py:

def raiser(*args, **kw):
    raise Exception("a fake error")
support.patch(self, email.generator.BytesGenerator, 'flatten', raiser)
with self.assertRaises(Exception):
    self._box.add(email.message_from_string("From: Alphöso"))

self.assertRaises(Exception) can happen for any other reason, we have zero confidence in the fact that we have the right exception. For example, a simple typo in email.message_from_stringX will also satisfy this check.

Sometimes, it is a bit better when assertRaisesRegex is used, then we have at least have a message to compare and be at least partially sure that the exception is correct. But, it is still not quite good, because we don't know the type of error that happen and it might accidentally change.

There are multiple places where this pattern is used:

Lib/test/test_ordered_dict.py
189:        with self.assertRaises(Exception):

Lib/test/test_importlib/test_main.py
71:        with self.assertRaises(Exception):

Lib/test/test_code.py
182:        with self.assertRaises(Exception):

Lib/test/test_shutil.py
2741:            with self.assertRaises(Exception):

Lib/test/test_unittest/testmock/testasync.py
464:        with self.assertRaises(Exception):

Lib/test/test_unittest/test_runner.py
349:        with self.assertRaises(Exception) as cm:
372:        with self.assertRaises(Exception) as cm:
382:        with self.assertRaises(Exception) as cm:
402:        with self.assertRaises(Exception) as e:
633:        with self.assertRaises(Exception) as e:
858:        with self.assertRaises(Exception) as cm:
891:        with self.assertRaises(Exception) as cm:
902:        with self.assertRaises(Exception) as cm:

Lib/test/test_concurrent_futures.py
1039:        with self.assertRaises(Exception) as cm:

Lib/test/test_mailbox.py
122:        with self.assertRaises(Exception):

Lib/test/test_yield_from.py
1121:            with self.assertRaises(Exception) as caught:
1385:            with self.assertRaises(Exception) as caught:
1397:            with self.assertRaises(Exception) as caught:
1411:            with self.assertRaises(Exception) as caught:
1423:            with self.assertRaises(Exception) as caught:
1435:            with self.assertRaises(Exception) as caught:
1507:            with self.assertRaises(Exception) as caught:

Lib/test/test_email/test_message.py
706:            with self.assertRaises(Exception) as ar:

Lib/test/test_codecs.py
2825:        with self.assertRaises(Exception) as failure:
2832:        with self.assertRaises(Exception) as failure:

Lib/test/test_mmap.py
703:        with self.assertRaises(Exception) as exc:

Lib/test/test_tarfile.py
2801:        with self.assertRaises(Exception) as exc:

I suggest to:

  1. Use custom exception classes where possible class MyExc(Exception): ..., this way we will have 100% guarantee that the test is accurate
  2. Use assertRaisesRegex more, where possible
  3. Keep some of the assertRaises(Exception) where it is legit, for example test_ordereddict says: Note, the exact exception raised is not guaranteed. The only guarantee that the next() will not succeed
  4. If as exc is used and there are isinstance checks - then we can keep it as is
  5. Keep it, where literally any exception can happen (or different ones)
  6. Keep it where Exception class is tested excplicitly, like in test_yield_from with g.throw(Exception)

I will send a PR with the fix.

Linked PRs

@sobolevn sobolevn added type-bug An unexpected behavior, bug, or error tests Tests in the Lib/test dir labels Jul 1, 2023
@sobolevn sobolevn self-assigned this Jul 1, 2023
@sobolevn
Copy link
Member Author

sobolevn commented Jul 1, 2023

And I found a real bug hidden under assertRaises(Exception) :)

This test

def test_doClassCleanups_with_errors_addClassCleanUp(self):
class TestableTest(unittest.TestCase):
def testNothing(self):
pass
def cleanup1():
raise Exception('cleanup1')
def cleanup2():
raise Exception('cleanup2')
TestableTest.addClassCleanup(cleanup1)
TestableTest.addClassCleanup(cleanup2)
with self.assertRaises(Exception) as e:
TestableTest.doClassCleanups()
self.assertEqual(e, 'cleanup1')
does something interesting:

with self.assertRaises(Exception) as e:
    TestableTest.doClassCleanups()
    self.assertEqual(e, 'cleanup1')

Reader might expect that TestableTest.doClassCleanups() raises some kind of exception, but it does not. What really happens is that e is not equal to 'cleanup1' and AssertionError happens inside self.assertRaises(Exception) and the tests passes successfully, but it is still broken!

I will fix unittest tests in a separate PR. There are quite a lot of usages of this pattern.

@serhiy-storchaka
Copy link
Member

Nice catch. Could you please fix also cases with assertRaises(BaseException)?

@sobolevn
Copy link
Member Author

sobolevn commented Jul 1, 2023

Yes, sure. Is it better to include BaseException changes into the existing PR or into a new one?

@serhiy-storchaka
Copy link
Member

Up to you.

brettcannon pushed a commit that referenced this issue Jul 7, 2023
…H-106302) (GH-106534)

gh-106300: Improve `assertRaises(Exception)` usages in tests (GH-106302)
(cherry picked from commit 6e6a4cd)

Co-authored-by: Nikita Sobolev <mail@sobolevn.me>
serhiy-storchaka pushed a commit to serhiy-storchaka/cpython that referenced this issue Jul 8, 2023
serhiy-storchaka added a commit to serhiy-storchaka/cpython that referenced this issue Jul 8, 2023
…ests (pythonGH-106302).

(cherry picked from commit 6e6a4cd)

Co-authored-by: Nikita Sobolev <mail@sobolevn.me>
serhiy-storchaka pushed a commit to serhiy-storchaka/cpython that referenced this issue Jul 8, 2023
…ests (pythonGH-106302).

(cherry picked from commit 6e6a4cd)

Co-authored-by: Nikita Sobolev <mail@sobolevn.me>
serhiy-storchaka added a commit that referenced this issue Jul 8, 2023
…H-106302). (GH-106545)

(cherry picked from commit 6e6a4cd)

Co-authored-by: Nikita Sobolev <mail@sobolevn.me>
sobolevn added a commit to sobolevn/cpython that referenced this issue Jul 14, 2023
serhiy-storchaka pushed a commit that referenced this issue Aug 16, 2023
…6737)

Use a custom exception to prevent unintentional silence of actual errors.
miss-islington pushed a commit to miss-islington/cpython that referenced this issue Aug 16, 2023
…ythonGH-106737)

Use a custom exception to prevent unintentional silence of actual errors.
(cherry picked from commit fd9d70a)

Co-authored-by: Nikita Sobolev <mail@sobolevn.me>
miss-islington pushed a commit to miss-islington/cpython that referenced this issue Aug 16, 2023
…ythonGH-106737)

Use a custom exception to prevent unintentional silence of actual errors.
(cherry picked from commit fd9d70a)

Co-authored-by: Nikita Sobolev <mail@sobolevn.me>
serhiy-storchaka pushed a commit that referenced this issue Aug 16, 2023
…GH-106737) (GH-108007)

Use a custom exception to prevent unintentional silence of actual errors.
(cherry picked from commit fd9d70a)

Co-authored-by: Nikita Sobolev <mail@sobolevn.me>
Yhg1s pushed a commit that referenced this issue Aug 16, 2023
…GH-106737) (#108006)

gh-106300: Improve errors testing in test_unittest.test_runner (GH-106737)

Use a custom exception to prevent unintentional silence of actual errors.
(cherry picked from commit fd9d70a)

Co-authored-by: Nikita Sobolev <mail@sobolevn.me>
@hugovk
Copy link
Member

hugovk commented Nov 10, 2023

Thanks for the fixes!

@hugovk hugovk closed this as completed Nov 10, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
tests Tests in the Lib/test dir type-bug An unexpected behavior, bug, or error
Projects
None yet
Development

No branches or pull requests

3 participants