summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Collins <robertc@robertcollins.net>2011-06-23 19:56:42 +1200
committerRobert Collins <robertc@robertcollins.net>2011-06-23 19:56:42 +1200
commit2fb7b0381ef2d6aec18cb278ab3ed3becc4d780d (patch)
tree7ee3c7221e2fd64e63ca6c101516394e53164d6b
parent6b5695a1e5b6969ba16ad382256005c211e281c7 (diff)
downloadfixtures-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--NEWS8
-rw-r--r--lib/fixtures/fixture.py29
-rw-r--r--lib/fixtures/testcase.py4
-rw-r--r--lib/fixtures/tests/test_fixture.py24
4 files changed, 58 insertions, 7 deletions
diff --git a/NEWS b/NEWS
index e0a17ec..54c4511 100644
--- a/NEWS
+++ b/NEWS
@@ -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.