From 853d1f7b23b0de1acc4f1762b8b9645a7afaa0d9 Mon Sep 17 00:00:00 2001 From: Robert Collins Date: Fri, 8 Jan 2010 15:35:16 +1100 Subject: Make public reusable functions for setting up and tearing down resources of a test. --- NEWS | 4 ++ lib/testresources/__init__.py | 80 +++++++++++++++------- .../tests/test_resourced_test_case.py | 12 ++-- 3 files changed, 66 insertions(+), 30 deletions(-) diff --git a/NEWS b/NEWS index 37d91f9..3cff2a8 100644 --- a/NEWS +++ b/NEWS @@ -17,6 +17,10 @@ BUG FIXES API CHANGES ~~~~~~~~~~~ +* New public functions testresources.setUpResources and + testresources.tearDownResources for folk that cannot easily use + ResourcedTestCase. Fixes bug #504146. + INTERNALS ~~~~~~~~~ diff --git a/lib/testresources/__init__.py b/lib/testresources/__init__.py index b9854a5..bed49b3 100644 --- a/lib/testresources/__init__.py +++ b/lib/testresources/__init__.py @@ -357,6 +357,13 @@ class TestResource(object): class ResourcedTestCase(unittest.TestCase): """A TestCase parent or utility that enables cross-test resource usage. + ResourcedTestCase is a thin wrapper around the + testresources.setUpResources and testresources.tearDownResources helper + functions. It should be trivially reimplemented where a different base + class is neded, or you can use multiple inheritance and call into + ResourcedTestCase.setUpResources and ResourcedTestCase.tearDownResources + from your setUp and tearDown (or whatever cleanup idiom is used). + :ivar resources: A list of (name, resource) pairs, where 'resource' is a subclass of `TestResource` and 'name' is the name of the attribute that the resource should be stored on. @@ -364,40 +371,61 @@ class ResourcedTestCase(unittest.TestCase): resources = [] - def __get_result(self): - # unittest hides the result. This forces us to look up the stack. - # The result is passed to a run() or a __call__ method 4 or more frames - # up: that method is what calls setUp and tearDown, and they call their - # parent setUp etc. Its not guaranteed that the parameter to run will - # be calls result as its not required to be a keyword parameter in - # TestCase. However, in practice, this works. - stack = inspect.stack() - for frame in stack[3:]: - if frame[3] in ('run', '__call__'): - # Not all frames called 'run' will be unittest. It could be a - # reactor in trial, for instance. - result = frame[0].f_locals.get('result') - if (result is not None and - getattr(result, 'startTest', None) is not None): - return result - def setUp(self): unittest.TestCase.setUp(self) self.setUpResources() def setUpResources(self): - """Set up any resources that this test needs.""" - result = self.__get_result() - for resource in self.resources: - setattr(self, resource[0], resource[1].getResource(result)) + setUpResources(self, self.resources, _get_result()) def tearDown(self): self.tearDownResources() unittest.TestCase.tearDown(self) def tearDownResources(self): - """Tear down any resources that this test declares.""" - result = self.__get_result() - for resource in self.resources: - resource[1].finishedWith(getattr(self, resource[0]), result) - delattr(self, resource[0]) + tearDownResources(self, self.resources, _get_result()) + + +def setUpResources(test, resources, result): + """Set up resources for test. + + :param test: The test to setup resources for. + :param resources: The resources to setup. + :param result: A result object for tracing resource activity. + """ + for resource in resources: + setattr(test, resource[0], resource[1].getResource(result)) + + +def tearDownResources(test, resources, result): + """Tear down resources for test. + + :param test: The test to tear down resources from. + :param resources: The resources to tear down. + :param result: A result object for tracing resource activity. + """ + for resource in resources: + resource[1].finishedWith(getattr(test, resource[0]), result) + delattr(test, resource[0]) + + +def _get_result(): + """Find a TestResult in the stack. + + unittest hides the result. This forces us to look up the stack. + The result is passed to a run() or a __call__ method 4 or more frames + up: that method is what calls setUp and tearDown, and they call their + parent setUp etc. Its not guaranteed that the parameter to run will + be calls result as its not required to be a keyword parameter in + TestCase. However, in practice, this works. + """ + stack = inspect.stack() + for frame in stack[2:]: + if frame[3] in ('run', '__call__'): + # Not all frames called 'run' will be unittest. It could be a + # reactor in trial, for instance. + result = frame[0].f_locals.get('result') + if (result is not None and + getattr(result, 'startTest', None) is not None): + return result + diff --git a/lib/testresources/tests/test_resourced_test_case.py b/lib/testresources/tests/test_resourced_test_case.py index ee0a4ca..f613f39 100644 --- a/lib/testresources/tests/test_resourced_test_case.py +++ b/lib/testresources/tests/test_resourced_test_case.py @@ -67,7 +67,8 @@ class TestResourcedTestCase(testtools.TestCase): def testSetUpResourcesSingle(self): # setUpResources installs the resources listed in ResourcedTestCase. self.resourced_case.resources = [("foo", self.resource_manager)] - self.resourced_case.setUpResources() + testresources.setUpResources(self.resourced_case, + self.resourced_case.resources, None) self.assertEqual(self.resource, self.resourced_case.foo) def testSetUpResourcesMultiple(self): @@ -75,7 +76,8 @@ class TestResourcedTestCase(testtools.TestCase): self.resourced_case.resources = [ ('foo', self.resource_manager), ('bar', MockResource('bar_resource'))] - self.resourced_case.setUpResources() + testresources.setUpResources(self.resourced_case, + self.resourced_case.resources, None) self.assertEqual(self.resource, self.resourced_case.foo) self.assertEqual('bar_resource', self.resourced_case.bar) @@ -86,14 +88,16 @@ class TestResourcedTestCase(testtools.TestCase): # Give the 'foo' resource access to a 'bar' resource self.resource_manager.resources.append( ('bar', MockResource('bar_resource'))) - self.resourced_case.setUpResources() + testresources.setUpResources(self.resourced_case, + self.resourced_case.resources, None) self.assertEqual(resource, self.resourced_case.foo) self.assertEqual('bar_resource', self.resourced_case.foo.bar) def testSetUpUsesResource(self): # setUpResources records a use of each declared resource. self.resourced_case.resources = [("foo", self.resource_manager)] - self.resourced_case.setUpResources() + testresources.setUpResources(self.resourced_case, + self.resourced_case.resources, None) self.assertEqual(self.resource_manager._uses, 1) def testTearDownResourcesDeletesResourceAttributes(self): -- cgit v1.2.1