Issue 15351: Add to unittest.TestCase support for using context managers (original) (raw)

Created on 2012-07-14 15:33 by chris.jerdonek, last changed 2022-04-11 14:57 by admin.

Files
File name Uploaded Description Edit
issue-15351-concept.patch chris.jerdonek,2012-07-16 20:53 review
Messages (9)
msg165454 - (view) Author: Chris Jerdonek (chris.jerdonek) * (Python committer) Date: 2012-07-14 15:33
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: `self._executeTestPart(testMethod, outcome, isTest=True)`). 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.
msg165466 - (view) Author: R. David Murray (r.david.murray) * (Python committer) Date: 2012-07-14 20:05
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 ;)
msg165467 - (view) Author: R. David Murray (r.david.murray) * (Python committer) Date: 2012-07-14 20:08
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.
msg165470 - (view) Author: Chris Jerdonek (chris.jerdonek) * (Python committer) Date: 2012-07-14 20:51
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. :)
msg165534 - (view) Author: Michael Foord (michael.foord) * (Python committer) Date: 2012-07-15 16:12
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.
msg165655 - (view) Author: Chris Jerdonek (chris.jerdonek) * (Python committer) Date: 2012-07-16 20:53
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.
msg168963 - (view) Author: Chris Jerdonek (chris.jerdonek) * (Python committer) Date: 2012-08-23 20:02
Adding Éric because of the interest in test setup and tear down in issue 11664.
msg186172 - (view) Author: Julian Berman (Julian) * Date: 2013-04-07 01:25
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)
msg400553 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2021-08-29 17:02
Opened for completely different approach to this problem.
History
Date User Action Args
2022-04-11 14:57:32 admin set github: 59556
2021-08-29 17:02:58 serhiy.storchaka set nosy: + serhiy.storchakamessages: +
2014-03-20 23:58:35 martin.panter set nosy: + martin.panter
2013-04-07 01:25:12 Julian set nosy: + Julianmessages: +
2013-04-02 22:58:22 cbc set nosy: + cbc
2013-02-11 21:04:28 michael.foord set assignee: michael.foord
2012-10-15 13:06:00 eli.bendersky set nosy: + eli.bendersky
2012-08-23 20:02:44 chris.jerdonek set nosy: + eric.araujomessages: +
2012-07-16 20:53:25 chris.jerdonek set files: + issue-15351-concept.patchkeywords: + patchmessages: +
2012-07-15 16:12:08 michael.foord set messages: +
2012-07-14 20:51:54 chris.jerdonek set messages: +
2012-07-14 20:08:22 r.david.murray set messages: +
2012-07-14 20:05:55 r.david.murray set nosy: + r.david.murray, michael.foordmessages: + versions: + Python 3.4
2012-07-14 15:33:51 chris.jerdonek create