-
-
Notifications
You must be signed in to change notification settings - Fork 32k
Add to unittest.TestCase support for using context managers #59556
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
Comments
The setUp() and tearDown() methods of unittest.TestCase are of course extremely useful. But sometimes one has set up and tear down functionality that one would like to apply in the form of an existing context manager (and that may be from an outside source). There is currently no clear or clean way to do this. It would be nice if unittest exposed a way to do this. The closest I have been able to come is overriding TestCase.run() as follows: class MyTest(unittest.TestCase):
def run(self, result=None):
with my_context_manager() as foo:
# Do stuff.
super(MyTest, self).run(result) But this is not ideal because the context manager is surrounding more than it should (various test initialization code internal to unittest, etc). Also, ideally the API would let one apply a context manager either before or after setUp() and tearDown(), or both. Here is one idea for an API. unittest.TestCase could expose a setUpContext() context manager that wraps the user-defined setUp() and tearDown(), and also a runTestMethod() method that runs the test method code by itself (i.e. currently the following line of unittest/case.py: To use the API, the user could override a simple method called something like runTest(): def runTest(self):
with setUpContext():
self.runTestMethod() The user would, in the override, be free to insert additional context managers before or after setUpContext(), or both. |
Well, if you want to invoke the context in setup/teardown for some reason (as opposed to in the test methods themselves), you can do this: def setUp(self):
self.foo = MyContextManager.__enter__()
self.addCleanup(MyContextManager.__exit__()) Personally I rarely do this (except occasionally the mock equivalent, see below), preferring to call the context manager explicitly in the test method itself, often factored out into a test helper. I think we as a community are still learning how best to use context managers and what the optimum design of context manager is. There is some thought that a context manager should always provide a non-context way of getting at the functionality of the enter and exit methods. For example, the mock context managers have start() and stop() methods. There has also been a small amount of discussion of making more use of context managers in unittest itself, possibly along the lines you suggest. I think this may be an area in which we are not yet ready to settle on an API. Michael may have a different opinion, of course ;) |
That should have been self.addCleanup(MyContextManager.__exit__) You could alternatively call __exit__() explicitly in tearDown, of course, but I believe addCleanup is a more reliable cleanup than tearDown. |
Thanks for the interesting background and feedback. I was aware of the __enter__/exit option but not the other information. And yes, I agree on the importance of trying and discussing any API before settling on it. The one I suggested was merely a point of departure. :) |
A method on TestCase that *just* executes the test method - allowing for overriding in subclasses - is an interesting idea. Including setUp and tearDown would be harder because TestCase necessarily does a bunch of bookkeeping between each of these steps. |
Attached is a patch illustrating the API I suggested for discussion. To add custom setup and teardown context managers, the user can override the following method: def executeTest(self):
with self.setUpContext():
self.runTestMethod() The custom context managers can be placed either before or after the existing setUp/tearDown, or both. The patch preserves the existing behavior that tearDown() should run only if setUp() was successful, and that doCleanups() should always run. All tests continue to pass with the patch. |
Adding Éric because of the interest in test setup and tear down in bpo-11664. |
Now that we have contextlib.ExitStack, I think we should consider that here. I.e., I think ExitStack deserves a method that calls its __enter__ and __exit__, say .enter() and .exit(), and then the idiom for this wouldn't require anything on TestCase, it'd be: class TestStuff(TestCase):
def setUp(self):
self.stack = ExitStack()
self.stack.enter_context(my_context_manager())
self.stack.enter_context(my_context_manager2())
self.stack.enter_context(my_context_manager3())
self.stack.enter()
self.addCleanup(self.stack.exit) |
Opened bpo-45046 for completely different approach to this problem. |
Do we still need this after implementing #89209? |
I'm closing this issue because there hasn't been any activity here in the last 10 years. |
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
bugs.python.org fields:
The text was updated successfully, but these errors were encountered: