diff options
author | Robert Collins <robertc@robertcollins.net> | 2012-04-04 22:02:46 +1200 |
---|---|---|
committer | Robert Collins <robertc@robertcollins.net> | 2012-04-04 22:02:46 +1200 |
commit | 45564bb7c1ee43448c09f72097988756ff56e8fc (patch) | |
tree | bdc9b6527007c760b5e1a3d767b293493bf413e6 | |
parent | e1339ea008f571b21ad13c5d69926ed6d66bb290 (diff) | |
parent | da26369a7f2c602587f01f122a79e0903815f78e (diff) | |
download | testscenarios-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-- | NEWS | 6 | ||||
-rw-r--r-- | README | 33 | ||||
-rw-r--r-- | lib/testscenarios/scenarios.py | 37 | ||||
-rw-r--r-- | lib/testscenarios/tests/__init__.py | 3 | ||||
-rw-r--r-- | lib/testscenarios/tests/test_scenarios.py | 22 |
5 files changed, 97 insertions, 4 deletions
@@ -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) @@ -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}), + ]) |