summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Pool <mbp@sourcefrog.net>2010-01-25 18:17:10 +0100
committerMartin Pool <mbp@sourcefrog.net>2010-01-25 18:17:10 +0100
commit6cfe52751cdaaf9f06713dde6fccb93f6c2f6744 (patch)
tree7f52cbe40472b71a3c5a44bff6842260c9e3b83a
parent364eb933798a089d6ee907dc50e8331a9d20d2de (diff)
downloadtestresources-6cfe52751cdaaf9f06713dde6fccb93f6c2f6744.tar.gz
Third-party documentation for testresources
-rw-r--r--README186
1 files changed, 153 insertions, 33 deletions
diff --git a/README b/README
index 89e9927..215a10f 100644
--- a/README
+++ b/README
@@ -1,4 +1,4 @@
-testresources: extensions to python unittest to allow declaritive use
+testresources: extensions to python unittest to allow declarative use
of resources by test cases.
Copyright (C) 2005-2008 Robert Collins <robertc@robertcollins.net>
@@ -23,8 +23,8 @@ provide test optimisation where expensive common resources are needed for test
cases - for example sample working trees for VCS systems, reference databases
for enterprise applications, or web servers ... let imagination run wild.
-Dependencies:
-=============
+Dependencies
+============
* Python 2.4+
* testtools
@@ -33,18 +33,108 @@ Note that testtools is required for *running* the tests for testresources. You
can use testresources in your own app without using testtools.
-How testresources works:
-========================
+How testresources Works
+=======================
-These are the main components to make testresources work:
+The basic idea of testresources is:
-1) testresources.TestResource
+* Tests declare the resources they need in a ``resources`` attribute.
+* When the test is run, the required resource objects are allocated (either
+ newly constructed, or reused), and assigned to attributes of the TestCase.
-A TestResource is an object that tests can use. Usually a subclass of
-testresources.TestResource, with the make and possibly the clean and isDirty
-methods overridden. These methods create, decide on reuse and free resources.
+testresources distinguishes a 'resource' (a subclass of ``TestResource``)
+which acts as a kind of factory, and a 'resource' which can be any kind of
+object returned from the resource class's ``getResource`` method.
-The 'resources' list on the TestResource object is used to declare
+Resources are either clean or dirty. Being clean means they have same state in
+all important ways as a newly constructed instance and they can therefore be
+safely reused.
+
+Main Classes
+============
+
+testresources.ResourcedTestCase
+-------------------------------
+
+By extending or mixing-in this class, tests can have necessary resources
+automatically allocated and disposed or recycled.
+
+ResourceTestCase can be used as a base class for tests, and when that is done
+tests will have their ``resources`` attribute automatically checked for resources
+by both OptimisingTestSuite and their own setUp() and tearDown() methods.
+(This allows tests to remain functional without needing this specific
+TestSuite as a container). Alternatively, you can call
+ResourceTestCase.setUpResources(self) and
+ResourceTestCase.tearDownResources(self) from your own classes setUp and
+tearDown and the same behaviour will be activated.
+
+To declare the use of a resource, set the ``resources`` attribute to a list of
+tuples of ``(attribute_name, resource_factory)``.
+
+During setUp, for each declared requriment, the test gains an attribute
+pointing to an allocated resource, which is the result of calling
+``resource_factory.getResource()``. ``finishedWith`` will be called on each
+resource during tearDown().
+
+For example::
+
+ class TestLog(testresources.TestCase):
+
+ resources = [('branch', BzrPopulatedBranch())]
+
+ def test_log(self):
+ show_log(self.branch, ...)
+
+testresources.TestResource
+--------------------------
+
+A TestResource is an object that tests can use to create resources. It can be
+overridden to manage different types of resources. Normally test code doesn't
+need to call any methods on it, as this will be arranged by the testresources
+machinery.
+
+When implementing a new ``TestResource`` subclass you should consider
+overriding these methods:
+
+``make``
+ Must be overridden in every concrete subclass.
+
+ Returns a new instance of the resource object
+ (the actual resource, not the TestResource). Doesn't need to worry about
+ reuse, which is taken care of separately. This method is only called when a
+ new resource is definitely needed.
+
+``clean``
+ Cleans up an existing resource instance, eg by deleting a directory or
+ closing a network connection. By default this does nothing, which may be
+ appropriate for resources that are automatically garbage collected.
+
+``reset``
+ Reset a no-longer-used dirty resource to a clean state. By default this
+ just discards it and creates a new one, but for some resources there may be a
+ faster way to reset them.
+
+``isDirty``
+ Check whether an existing resource is dirty. By default this just reports whether
+ ``TestResource.dirtied`` has been called.
+
+For instance::
+
+ class TemporaryDirectoryResource(TestResource):
+
+ def clean(self, resource):
+ osutils.rmtree(resource)
+
+ def make(self):
+ return tempfile.mkdtemp()
+
+ def isDirty(self, resource):
+ # Can't detect when the directory is written to, so assume it
+ # can never be reused. We could list the directory, but that might
+ # not catch it being open as a cwd etc.
+ return True
+
+The ``resources`` list on the TestResource object is used to declare
dependencies. For instance, a DataBaseResource that needs a TemporaryDirectory
might be declared with a resources list::
@@ -53,8 +143,8 @@ might be declared with a resources list::
resources = [("scratchdir", TemporaryDirectoryResource())]
Most importantly, two getResources to the same TestResource with no
-finishedWith call in the middle, will return the same object as long as it has
-not been marked dirty.
+finishedWith call in the middle, will return the same object as long as it is
+not dirty.
When a Test has a dependency and that dependency successfully completes but
returns None, the framework does *not* consider this an error: be sure to always
@@ -65,8 +155,13 @@ A sample TestResource can be found in the doc/ folder.
See pydoc testresources.TestResource for details.
+testresources.GenericResource
+-----------------------------
+
+Glue to adapt testresources to an existing resource-like class.
-2) testresources.OptimisingTestSuite
+testresources.OptimisingTestSuite
+---------------------------------
This TestSuite will introspect all the test cases it holds directly and if
they declare needed resources, will run the tests in an order that attempts to
@@ -87,31 +182,56 @@ getting global optimisation or you could use several smaller
OptimisingTestSuites.
-3) testresources.ResourcedTestCase
-
-ResourceTestCase can be used as a base class for tests, and when that is done
-tests will have their resources attribute automatically checked for resources
-by both OptimisingTestSuite and their own setUp() and tearDown() methods.
-(This allows tests to remain functional without needing this specific
-TestSuite as a container). Alternatively, you can call
-ResourceTestCase.setUpResources(self) and
-ResourceTestCase.tearDownResources(self) from your own classes setUp and
-tearDown and the same behaviour will be activated.
-
-To declare the use of a resource, set resources as an attribute listing tuples
-of (attribute name, TestResource). During setUp, self._attribute_name will be
-set to TestResource.getResource(), and finishedWith() will be called for you
-during tearDown().
-
-
-4) testresources.TestLoader
+testresources.TestLoader
+------------------------
This is a trivial TestLoader that creates OptimisingTestSuites by default.
-5) unittest.TestResult
+unittest.TestResult
+-------------------
testresources will log activity about resource creation and destruction to the
result object tests are run with. 4 extension methods are looked for:
``startCleanResource``, ``stopCleanResource``, ``startMakeResource``,
``stopMakeResource``. ``testresources.tests.ResultWithResourceExtensions`` is
an example of a ``TestResult`` with these methods present.
+
+Controlling Resource Reuse
+==========================
+
+When or how do I mark the resource dirtied?
+
+The simplest approach is to have ``TestResource.make`` call ``self.dirtied``:
+the resource is always immediately dirty and will never be reused without first
+being reset. This is appropriate when the underlying resource is cheap to
+reset or recreate, or when it's hard to detect whether it's been dirtied or to
+trap operations that change it.
+
+Alternatively, override ``TestResource.isDirty`` and inspect the resource to
+see if it is safe to reuse.
+
+Finally, you can arrange for the returned resource to always call back to
+``TestResource.dirtied`` on the first operation that mutates it.
+
+FAQ
+===
+
+* Can I dynamically request resources inside a test method?
+
+ Generally, no, you shouldn't do this. The idea is that the resources are
+ declared statically, so that testresources can "smooth" resource usage across
+ several tests.
+
+* Isn't using the same word 'resource' for the TestResource and the resource
+ confusing?
+
+ Yes.
+
+* If the resource is held inside the TestResource object, and the TestResource
+ is typically constructed inline in the test case ``resources`` attribute, how
+ can they be shared across different test classes?
+
+ Good question.
+
+ I guess you should arrange for a single instance to be held in an appropriate
+ module scope, then referenced by the test classes that want to share it.