diff options
author | Robert Collins <robertc@robertcollins.net> | 2011-06-23 19:56:42 +1200 |
---|---|---|
committer | Robert Collins <robertc@robertcollins.net> | 2011-06-23 19:56:42 +1200 |
commit | 2fb7b0381ef2d6aec18cb278ab3ed3becc4d780d (patch) | |
tree | 7ee3c7221e2fd64e63ca6c101516394e53164d6b | |
parent | 6b5695a1e5b6969ba16ad382256005c211e281c7 (diff) | |
download | fixtures-2fb7b0381ef2d6aec18cb278ab3ed3becc4d780d.tar.gz |
Another small API break - sorry. Fixture.getDetails no longer returns the
internal details dict (self._details). Access that directly if needed. It now
looks for details in used fixtures and returns those as well as details added
directly.
(RobertCollins, #780806)
-rw-r--r-- | NEWS | 8 | ||||
-rw-r--r-- | lib/fixtures/fixture.py | 29 | ||||
-rw-r--r-- | lib/fixtures/testcase.py | 4 | ||||
-rw-r--r-- | lib/fixtures/tests/test_fixture.py | 24 |
4 files changed, 58 insertions, 7 deletions
@@ -6,6 +6,11 @@ fixtures release notes IN DEVELOPMENT ~~~~~~~~~~~~~~ +Another small API break - sorry. Fixture.getDetails no longer returns the +internal details dict (self._details). Access that directly if needed. It now +looks for details in used fixtures and returns those as well as details added +directly. + CHANGES: * Details from child fixtures for both Fixture and TestWithFixtures no longer @@ -13,6 +18,9 @@ CHANGES: gather_details helper). (Gavin Panella, #796253) +* Fixture.getDetails will now return all the details of fixtures added using + useFixture. (RobertCollins, #780806) + * New fixture ``PackagePathEntry`` which patches the path of an existing package, allowing importing part of it from aonther directory. (Robert Collins) diff --git a/lib/fixtures/fixture.py b/lib/fixtures/fixture.py index c42bc44..14da061 100644 --- a/lib/fixtures/fixture.py +++ b/lib/fixtures/fixture.py @@ -20,6 +20,7 @@ __all__ = [ 'MultipleExceptions', ] +import itertools import sys from testtools.helpers import try_import @@ -32,6 +33,17 @@ MultipleExceptions = try_import( gather_details = try_import("testtools.testcase.gather_details") +# This would be better in testtools (or a common library) +def combine_details(source_details, target_details): + """Add every value from source to target deduping common keys.""" + for name, content_object in source_details.items(): + new_name = name + disambiguator = itertools.count(1) + while new_name in target_details: + new_name = '%s-%d' % (name, advance_iterator(disambiguator)) + name = new_name + target_details[name] = content_object + class Fixture(object): """A Fixture representing some state or resource. @@ -120,6 +132,7 @@ class Fixture(object): """ self._cleanups = [] self._details = {} + self._detail_sources = [] def __enter__(self): self.setUp() @@ -132,12 +145,15 @@ class Fixture(object): def getDetails(self): """Get the current details registered with the fixture. - This returns the internal dictionary: mutating it is supported (but not - encouraged). + This does not return the internal dictionary: mutating it will have no + effect. If you need to mutate it, just do so directly. :return: Dict from name -> content_object. """ - return self._details + result = dict(self._details) + for source in self._detail_sources: + combine_details(source.getDetails(), result) + return result def setUp(self): """Prepare the Fixture for use. @@ -180,13 +196,16 @@ class Fixture(object): try: fixture.setUp() except: + # The child failed to come up, capture any details it has (copying + # the content, it may go away anytime). if gather_details is not None: gather_details(fixture, self) raise else: self.addCleanup(fixture.cleanUp) - if gather_details is not None: - self.addCleanup(gather_details, fixture, self) + # Calls to getDetails while this fixture is setup will return + # details from the child fixture. + self._detail_sources.append(fixture) return fixture diff --git a/lib/fixtures/testcase.py b/lib/fixtures/testcase.py index 38fc4ff..0a6bb61 100644 --- a/lib/fixtures/testcase.py +++ b/lib/fixtures/testcase.py @@ -44,10 +44,14 @@ class TestWithFixtures(unittest.TestCase): fixture.setUp() except: if use_details: + # Capture the details now, in case the fixture goes away. gather_details(fixture, self) raise else: self.addCleanup(fixture.cleanUp) if use_details: + # Capture the details from the fixture during test teardown; + # this will evaluate the details before tearing down the + # fixture. self.addCleanup(gather_details, fixture, self) return fixture diff --git a/lib/fixtures/tests/test_fixture.py b/lib/fixtures/tests/test_fixture.py index 28afe75..ed4edf0 100644 --- a/lib/fixtures/tests/test_fixture.py +++ b/lib/fixtures/tests/test_fixture.py @@ -25,6 +25,10 @@ from fixtures.fixture import gather_details from fixtures.tests.helpers import LoggingFixture +require_gather_details = skipIf(gather_details is None, + "gather_details() is not available.") + + class TestFixture(testtools.TestCase): def test_resetCallsSetUpCleanUp(self): @@ -114,7 +118,7 @@ class TestFixture(testtools.TestCase): ['setUp-outer', 'setUp-inner', 'cleanUp-inner', 'cleanUp-outer'], parent.calls) - @skipIf(gather_details is None, "gather_details() is not available.") + @require_gather_details def test_useFixture_details_captured_from_setUp(self): # Details added during fixture set-up are gathered even if setUp() # fails with an exception. @@ -143,12 +147,28 @@ class TestFixture(testtools.TestCase): with fixture: self.assertEqual({}, fixture.getDetails()) + def test_details_from_child_fixtures_are_returned(self): + parent = fixtures.Fixture() + with parent: + child = fixtures.Fixture() + parent.useFixture(child) + # Note that we add the detail *after* using the fixture: the parent + # has to query just-in-time. + child.addDetail('foo', 'content') + self.assertEqual({'foo': 'content'}, parent.getDetails()) + # And dropping it from the child drops it from the parent. + del child._details['foo'] + self.assertEqual({}, parent.getDetails()) + # After cleanup the child details are still gone. + child.addDetail('foo', 'content') + self.assertEqual({}, parent.getDetails()) + def test_addDetail(self): fixture = fixtures.Fixture() with fixture: fixture.addDetail('foo', 'content') self.assertEqual({'foo': 'content'}, fixture.getDetails()) - del fixture.getDetails()['foo'] + del fixture._details['foo'] self.assertEqual({}, fixture.getDetails()) fixture.addDetail('foo', 'content') # Cleanup clears the details too. |