Opened 4 years ago
Last modified 4 years ago
#31944 closed Cleanup/optimization
Use addCleanup() to register TestContextDecorator cleanups — at Initial Version
Reported by: | François Freitag | Owned by: | nobody |
---|---|---|---|
Component: | Testing framework | Version: | dev |
Severity: | Normal | Keywords: | |
Cc: | Triage Stage: | Ready for checkin | |
Has patch: | yes | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
unittest.TestCase
offers an addCleanup() function that is guaranteed to run even if unittest.TestCase.setUp()
fails.
In #29024, the cleanup was implemented by catching Exception, calling TestContextDecorator.disable() and re-raising. Using addCleanup()
allows a small simplification. More importantly, it prevents an ordering issue with the cleanups that can cause tests to bleed when subclasses of Django TestCase
relying of addCleanup()
have their cleanups occurring after the TestContextDecorator.disable()
. To illustrate:
class ChangeCSRFCookieName(TestContextDecorator): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.stack = contextlib.ExitStack() def enable(self): cm = override_settings(CSRF_COOKIE_NAME="cross_site_request_forgery_token") self.stack.enter_context(cm) def disable(self): self.stack.close() class ChangeSettingsTestCase(SimpleTestCase): def setUp(self): super().setUp() settings_cm = self.settings(CACHES={}) settings_cm.enable() self.addCleanup(settings_cm.disable) @ChangeCSRFCookieName() class ChangeCSRFCookieNameTest(ChangeSettingsTestCase): def test_initial(self): pass class LeakTest(SimpleTestCase): def test_csrf_cookie_name_has_leaked(self): # Should use the default value. self.assertEqual("csrftoken", settings.CSRF_COOKIE_NAME) # Fails with: #====================================================================== #FAIL: test_csrf_cookie_name_has_leaked (test_utils.tests.LeakTest) #---------------------------------------------------------------------- #Traceback (most recent call last): # File "/home/freitafr/dev/django-side/tests/test_utils/tests.py", line 1514, in test_csrf_cookie_name_has_leaked # self.assertEqual("csrftoken", settings.CSRF_COOKIE_NAME) #AssertionError: 'csrftoken' != 'cross_site_request_forgery_token' #- csrftoken #+ cross_site_request_forgery_token
The cleanups from addCleanup()
are scheduled to happen in reverse order to the order they are added (LIFO). That should make sure each cleanup is executed from the innermost to the outermost.
I am happy to submit a patch for the fix, but I am concerned the change may be backward incompatible. Perhaps people relied on this ordering, and “fixing” the ordering will trigger test bleeding in their test suite. Is TestContextDecorator
considered private enough to be changed with a mention in the release note?