From 50d71aaaf387ad7dc259eb291d2860a0b946fd8c Mon Sep 17 00:00:00 2001 From: Robert Collins Date: Sun, 8 Mar 2009 15:08:51 +1100 Subject: Write generate_scenarios. --- lib/testscenarios/__init__.py | 6 ++ lib/testscenarios/scenarios.py | 50 +++++++++++++++ lib/testscenarios/testcase.py | 11 ++-- lib/testscenarios/tests/__init__.py | 1 + lib/testscenarios/tests/test_scenarios.py | 100 ++++++++++++++++++++++++++++++ 5 files changed, 161 insertions(+), 7 deletions(-) create mode 100644 lib/testscenarios/scenarios.py create mode 100644 lib/testscenarios/tests/test_scenarios.py diff --git a/lib/testscenarios/__init__.py b/lib/testscenarios/__init__.py index 57b49b4..4e0bf5d 100644 --- a/lib/testscenarios/__init__.py +++ b/lib/testscenarios/__init__.py @@ -29,9 +29,15 @@ See the README for a manual, and the docstrings on individual functions and methods for details. """ +__all__ = [ + 'TestWithScenarios', + 'generate_scenarios', + ] + import unittest +from testscenarios.scenarios import generate_scenarios from testscenarios.testcase import TestWithScenarios diff --git a/lib/testscenarios/scenarios.py b/lib/testscenarios/scenarios.py new file mode 100644 index 0000000..bbc73ad --- /dev/null +++ b/lib/testscenarios/scenarios.py @@ -0,0 +1,50 @@ +# testscenarios: extensions to python unittest to allow declarative +# dependency injection ('scenarios') by tests. +# Copyright (C) 2009 Robert Collins +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +__all__ = [ + 'generate_scenarios', + ] + +import unittest + +from testtools.testcase import clone_test_with_new_id +from testtools import iterate_tests + +def generate_scenarios(test_or_suite): + """Yield the tests in test_or_suite with scenario multiplication done. + + TestCase objects with no scenarios specified are yielded unaltered. Tests + with scenarios are not yielded at all, instead the results of multiplying + them by the scenarios they specified gets yielded. + + :param test_or_suite: A TestCase or TestSuite. + :return: A generator of tests - objects satisfying the TestCase protocol. + """ + for test in iterate_tests(test_or_suite): + scenarios = getattr(test, 'scenarios', None) + if scenarios: + for name, parameters in scenarios: + newtest = clone_test_with_new_id(test, + test.id() + '(' + name + ')') + newtest.scenarios = None + for key, value in parameters.iteritems(): + setattr(newtest, key, value) + yield newtest + else: + yield test diff --git a/lib/testscenarios/testcase.py b/lib/testscenarios/testcase.py index ec27e39..712d543 100644 --- a/lib/testscenarios/testcase.py +++ b/lib/testscenarios/testcase.py @@ -25,6 +25,8 @@ import unittest from testtools.testcase import clone_test_with_new_id +from testscenarios.scenarios import generate_scenarios + class TestWithScenarios(unittest.TestCase): """A TestCase with support for scenarios via a scenarios attribute. @@ -38,13 +40,8 @@ class TestWithScenarios(unittest.TestCase): def run(self, result=None): scenarios = getattr(self, 'scenarios', None) if scenarios: - for name, parameters in scenarios: - newtest = clone_test_with_new_id(self, - self.id() + '(' + name + ')') - newtest.scenarios = None - for key, value in parameters.iteritems(): - setattr(newtest, key, value) - newtest.run(result) + for test in generate_scenarios(self): + test.run(result) return else: return super(TestWithScenarios, self).run(result) diff --git a/lib/testscenarios/tests/__init__.py b/lib/testscenarios/tests/__init__.py index 1777e74..9bce8a3 100644 --- a/lib/testscenarios/tests/__init__.py +++ b/lib/testscenarios/tests/__init__.py @@ -34,6 +34,7 @@ def test_suite(): def load_tests(standard_tests, module, loader): test_modules = [ 'testcase', + 'scenarios', ] prefix = "testscenarios.tests.test_" test_mod_names = [prefix + test_module for test_module in test_modules] diff --git a/lib/testscenarios/tests/test_scenarios.py b/lib/testscenarios/tests/test_scenarios.py new file mode 100644 index 0000000..b019d97 --- /dev/null +++ b/lib/testscenarios/tests/test_scenarios.py @@ -0,0 +1,100 @@ +# testscenarios: extensions to python unittest to allow declarative +# dependency injection ('scenarios') by tests. +# Copyright (C) 2009 Robert Collins +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +import unittest + +import testscenarios +from testscenarios.scenarios import generate_scenarios +from testtools.tests.helpers import LoggingResult + + +class TestGenerateScenarios(unittest.TestCase): + + def test_generate_scenarios_preserves_normal_test(self): + class ReferenceTest(unittest.TestCase): + def test_pass(self): + pass + test = ReferenceTest("test_pass") + self.assertEqual([test], list(generate_scenarios(test))) + + def test_normal_test_with_one_scenario(self): + class ReferenceTest(unittest.TestCase): + scenarios = [('demo', {})] + def test_pass(self): + pass + test = ReferenceTest("test_pass") + tests = list(generate_scenarios(test)) + self.assertEqual( + 'testscenarios.tests.test_scenarios.ReferenceTest.test_pass(demo)', + tests[0].id()) + + def test_normal_test_two_scenarios(self): + class ReferenceTest(unittest.TestCase): + scenarios = [('1', {}), ('2', {})] + def test_pass(self): + pass + test = ReferenceTest("test_pass") + tests = list(generate_scenarios(test)) + self.assertEqual( + 'testscenarios.tests.test_scenarios.ReferenceTest.test_pass(1)', + tests[0].id()) + self.assertEqual( + 'testscenarios.tests.test_scenarios.ReferenceTest.test_pass(2)', + tests[1].id()) + + def test_attributes_set(self): + class ReferenceTest(unittest.TestCase): + scenarios = [ + ('1', {'foo': 1, 'bar': 2}), + ('2', {'foo': 2, 'bar': 4})] + def test_check_foo(self): + pass + test = ReferenceTest("test_check_foo") + tests = list(generate_scenarios(test)) + self.assertEqual(1, tests[0].foo) + self.assertEqual(2, tests[0].bar) + self.assertEqual(2, tests[1].foo) + self.assertEqual(4, tests[1].bar) + + def test_scenarios_attribute_cleared(self): + class ReferenceTest(unittest.TestCase): + scenarios = [ + ('1', {'foo': 1, 'bar': 2}), + ('2', {'foo': 2, 'bar': 4})] + def test_check_foo(self): + pass + test = ReferenceTest("test_check_foo") + tests = list(generate_scenarios(test)) + for adapted in tests: + self.assertEqual(None, adapted.scenarios) + + def test_multiple_tests(self): + class Reference1(unittest.TestCase): + scenarios = [('1', {}), ('2', {})] + def test_something(self): + pass + class Reference2(unittest.TestCase): + scenarios = [('3', {}), ('4', {})] + def test_something(self): + pass + suite = unittest.TestSuite() + suite.addTest(Reference1("test_something")) + suite.addTest(Reference2("test_something")) + tests = list(generate_scenarios(suite)) + self.assertEqual(4, len(tests)) -- cgit v1.2.1