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

Add to unittest.TestCase support for using context managers #59556

Closed
cjerdonek opened this issue Jul 14, 2012 · 11 comments
Closed

Add to unittest.TestCase support for using context managers #59556

cjerdonek opened this issue Jul 14, 2012 · 11 comments
Assignees
Labels
stdlib Python modules in the Lib dir type-feature A feature request or enhancement

Comments

@cjerdonek
Copy link
Member

BPO 15351
Nosy @merwok, @bitdancer, @voidspace, @cjerdonek, @Julian, @vadmium, @serhiy-storchaka
Files
  • issue-15351-concept.patch
  • 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:

    assignee = 'https://github.com/voidspace'
    closed_at = None
    created_at = <Date 2012-07-14.15:33:51.733>
    labels = ['type-feature', 'library']
    title = 'Add to unittest.TestCase support for using context managers'
    updated_at = <Date 2021-08-29.17:02:58.886>
    user = 'https://github.com/cjerdonek'

    bugs.python.org fields:

    activity = <Date 2021-08-29.17:02:58.886>
    actor = 'serhiy.storchaka'
    assignee = 'michael.foord'
    closed = False
    closed_date = None
    closer = None
    components = ['Library (Lib)']
    creation = <Date 2012-07-14.15:33:51.733>
    creator = 'chris.jerdonek'
    dependencies = []
    files = ['26405']
    hgrepos = []
    issue_num = 15351
    keywords = ['patch']
    message_count = 9.0
    messages = ['165454', '165466', '165467', '165470', '165534', '165655', '168963', '186172', '400553']
    nosy_count = 9.0
    nosy_names = ['eric.araujo', 'r.david.murray', 'michael.foord', 'eli.bendersky', 'chris.jerdonek', 'Julian', 'martin.panter', 'serhiy.storchaka', 'cbc']
    pr_nums = []
    priority = 'normal'
    resolution = None
    stage = None
    status = 'open'
    superseder = None
    type = 'enhancement'
    url = 'https://bugs.python.org/issue15351'
    versions = ['Python 3.4']

    @cjerdonek
    Copy link
    Member Author

    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.

    @cjerdonek cjerdonek added stdlib Python modules in the Lib dir type-feature A feature request or enhancement labels Jul 14, 2012
    @bitdancer
    Copy link
    Member

    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 ;)

    @bitdancer
    Copy link
    Member

    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.

    @cjerdonek
    Copy link
    Member Author

    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. :)

    @voidspace
    Copy link
    Contributor

    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.

    @cjerdonek
    Copy link
    Member Author

    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.

    @cjerdonek
    Copy link
    Member Author

    Adding Éric because of the interest in test setup and tear down in bpo-11664.

    @voidspace voidspace self-assigned this Feb 11, 2013
    @Julian
    Copy link
    Mannequin

    Julian mannequin commented Apr 7, 2013

    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)

    @serhiy-storchaka
    Copy link
    Member

    Opened bpo-45046 for completely different approach to this problem.

    @ezio-melotti ezio-melotti transferred this issue from another repository Apr 10, 2022
    @serhiy-storchaka
    Copy link
    Member

    Do we still need this after implementing #89209?

    @serhiy-storchaka
    Copy link
    Member

    I'm closing this issue because there hasn't been any activity here in the last 10 years.

    @serhiy-storchaka serhiy-storchaka closed this as not planned Won't fix, can't repro, duplicate, stale Sep 9, 2023
    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
    Labels
    stdlib Python modules in the Lib dir type-feature A feature request or enhancement
    Projects
    Status: Done
    Development

    No branches or pull requests

    4 participants