summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Collins <robertc@robertcollins.net>2012-04-04 22:02:46 +1200
committerRobert Collins <robertc@robertcollins.net>2012-04-04 22:02:46 +1200
commit45564bb7c1ee43448c09f72097988756ff56e8fc (patch)
treebdc9b6527007c760b5e1a3d767b293493bf413e6
parente1339ea008f571b21ad13c5d69926ed6d66bb290 (diff)
parentda26369a7f2c602587f01f122a79e0903815f78e (diff)
downloadtestscenarios-git-45564bb7c1ee43448c09f72097988756ff56e8fc.tar.gz
* New function ``per_module_scenarios`` for tests that should be applied across
multiple modules providing the same interface, some of which may not be available at run time. (Martin Pool, Robert Collins)
-rw-r--r--NEWS6
-rw-r--r--README33
-rw-r--r--lib/testscenarios/scenarios.py37
-rw-r--r--lib/testscenarios/tests/__init__.py3
-rw-r--r--lib/testscenarios/tests/test_scenarios.py22
5 files changed, 97 insertions, 4 deletions
diff --git a/NEWS b/NEWS
index db185e2..bdcf729 100644
--- a/NEWS
+++ b/NEWS
@@ -9,6 +9,12 @@ IN DEVELOPMENT
0.2
~~~
+NEW FEATURES:
+
+* New function ``per_module_scenarios`` for tests that should be applied across
+ multiple modules providing the same interface, some of which may not be
+ available at run time. (Martin Pool)
+
CHANGES:
* Adjust the cloned tests ``shortDescription`` if one is present. (Ben Finney)
diff --git a/README b/README
index 887a6ad..002c340 100644
--- a/README
+++ b/README
@@ -111,7 +111,7 @@ implementations::
>>> mytests = loader.loadTestsFromNames(['doc.test_sample'])
>>> test_suite.addTests(generate_scenarios(mytests))
>>> runner.run(test_suite)
- <unittest._TextTestResult run=1 errors=0 failures=0>
+ <unittest...TextTestResult run=1 errors=0 failures=0>
Testloaders
+++++++++++
@@ -258,6 +258,37 @@ allowing it to be used to layer scenarios without affecting existing scenario
selection.
+Generating Scenarios
+====================
+
+Some functions (currently one :-) are available to ease generation of scenario
+lists for common situations.
+
+Testing Per Implementation Module
++++++++++++++++++++++++++++++++++
+
+It is reasonably common to have multiple Python modules that provide the same
+capabilities and interface, and to want apply the same tests to all of them.
+
+In some cases, not all of the statically defined implementations will be able
+to be used in a particular testing environment. For example, there may be both
+a C and a pure-Python implementation of a module. You want to test the C
+module if it can be loaded, but also to have the tests pass if the C module has
+not been compiled.
+
+The ``per_module_scenarios`` function generates a scenario for each named
+module. The module object of the imported module is set in the supplied
+attribute name of the resulting scenario.
+Modules which raise ``ImportError`` during import will have the
+``sys.exc_info()`` of the exception set instead of the module object. Tests
+can check for the attribute being a tuple to decide what to do (e.g. to skip).
+
+Note that for the test to be valid, all access to the module under test must go
+through the relevant attribute of the test object. If one of the
+implementations is also directly imported by the test module or any other,
+testscenarios will not magically stop it being used.
+
+
Advice on Writing Scenarios
===========================
diff --git a/lib/testscenarios/scenarios.py b/lib/testscenarios/scenarios.py
index 80847d6..9538b33 100644
--- a/lib/testscenarios/scenarios.py
+++ b/lib/testscenarios/scenarios.py
@@ -2,7 +2,7 @@
# dependency injection ('scenarios') by tests.
#
# Copyright (c) 2009, Robert Collins <robertc@robertcollins.net>
-# Copyright (c) 2010 Martin Pool <mbp@sourcefrog.net>
+# Copyright (c) 2010, 2011 Martin Pool <mbp@sourcefrog.net>
#
# Licensed under either the Apache License, Version 2.0 or the BSD 3-clause
# license at the users choice. A copy of both licenses are available in the
@@ -27,6 +27,7 @@ from itertools import (
chain,
product,
)
+import sys
import unittest
from testtools.testcase import clone_test_with_new_id
@@ -129,3 +130,37 @@ def multiply_scenarios(*scenarios):
scenario_parameters.update(parameter)
result.append((scenario_name, scenario_parameters))
return result
+
+
+def per_module_scenarios(attribute_name, modules):
+ """Generate scenarios for available implementation modules.
+
+ This is typically used when there is a subsystem implemented, for
+ example, in both Python and C, and we want to apply the same tests to
+ both, but the C module may sometimes not be available.
+
+ Note: if the module can't be loaded, the sys.exc_info() tuple for the
+ exception raised during import of the module is used instead of the module
+ object. A common idiom is to check in setUp for that and raise a skip or
+ error for that case. No special helpers are supplied in testscenarios as
+ yet.
+
+ :param attribute_name: A name to be set in the scenario parameter
+ dictionary (and thence onto the test instance) pointing to the
+ implementation module (or import exception) for this scenario.
+
+ :param modules: An iterable of (short_name, module_name), where
+ the short name is something like 'python' to put in the
+ scenario name, and the long name is a fully-qualified Python module
+ name.
+ """
+ scenarios = []
+ for short_name, module_name in modules:
+ try:
+ mod = __import__(module_name, {}, {}, [''])
+ except:
+ mod = sys.exc_info()
+ scenarios.append((
+ short_name,
+ {attribute_name: mod}))
+ return scenarios
diff --git a/lib/testscenarios/tests/__init__.py b/lib/testscenarios/tests/__init__.py
index e5e2bbe..6230212 100644
--- a/lib/testscenarios/tests/__init__.py
+++ b/lib/testscenarios/tests/__init__.py
@@ -38,5 +38,6 @@ def load_tests(standard_tests, module, loader):
test_mod_names = [prefix + test_module for test_module in test_modules]
standard_tests.addTests(loader.loadTestsFromNames(test_mod_names))
doctest.set_unittest_reportflags(doctest.REPORT_ONLY_FIRST_FAILURE)
- standard_tests.addTest(doctest.DocFileSuite("../../../README"))
+ standard_tests.addTest(
+ doctest.DocFileSuite("../../../README", optionflags=doctest.ELLIPSIS))
return standard_tests
diff --git a/lib/testscenarios/tests/test_scenarios.py b/lib/testscenarios/tests/test_scenarios.py
index 063df51..97aa17f 100644
--- a/lib/testscenarios/tests/test_scenarios.py
+++ b/lib/testscenarios/tests/test_scenarios.py
@@ -2,7 +2,7 @@
# dependency injection ('scenarios') by tests.
#
# Copyright (c) 2009, Robert Collins <robertc@robertcollins.net>
-# Copyright (c) 2010 Martin Pool <mbp@sourcefrog.net>
+# Copyright (c) 2010, 2011 Martin Pool <mbp@sourcefrog.net>
#
# Licensed under either the Apache License, Version 2.0 or the BSD 3-clause
# license at the users choice. A copy of both licenses are available in the
@@ -239,3 +239,23 @@ class TestMultiplyScenarios(testtools.TestCase):
self.assertEqual(
'a,a,a,a',
scenarios[0][0])
+
+
+class TestPerModuleScenarios(testtools.TestCase):
+
+ def test_per_module_scenarios(self):
+ """Generate scenarios for available modules"""
+ s = testscenarios.scenarios.per_module_scenarios(
+ 'the_module', [
+ ('Python', 'testscenarios'),
+ ('unittest', 'unittest'),
+ ('nonexistent', 'nonexistent'),
+ ])
+ self.assertEqual('nonexistent', s[-1][0])
+ self.assertIsInstance(s[-1][1]['the_module'], tuple)
+ s[-1][1]['the_module'] = None
+ self.assertEqual(s, [
+ ('Python', {'the_module': testscenarios}),
+ ('unittest', {'the_module': unittest}),
+ ('nonexistent', {'the_module': None}),
+ ])