comparison MoinMoin/_tests/__init__.py @ 2006:1339d6fdc4ff

Changed tests to use py.test. Also changed the semantics a bit - the test wiki directory is only created freshly if it does not exist.
author Alexander Schremmer <alex AT alexanderweb DOT de>
date Fri, 20 Apr 2007 00:12:50 +0200
parents bb2e053067fb
children 02b73ceab324
comparison
equal deleted inserted replaced
2005:511924c36086 2006:1339d6fdc4ff
1 # -*- coding: iso-8859-1 -*-
2 """
3 MoinMoin Testing Framework
4 --------------------------
5
6 All test modules must be named test_modulename to be included in the
7 test suit. If you are testing a package, name the test module
8 test_pakcage_module.
9
10 Each test_ module may contain test case classes sub classed from
11 unittest.TestCase or subclass of it. Previous versions required TestCase
12 suffix, but now its only a convention.
13
14 Each test case class may contain multiply test methods, that must start
15 with 'test'. Those methods will be run by the test suites.
16
17 Test that need the current request, for example to create a page
18 instance, can refer to self.request. It is injected into all test case
19 classes by the framework.
20
21 Tests that require certain configuration, like section_numbers = 1, must
22 use a TestConfig to create the required configuration before the
23 test. Deleting the TestConfig instance will restore the previous
24 configuration.
25
26 See _test_template.py for an example, and use it to create new tests.
27
28 Typical Usage
29 -------------
30
31 Running all test in this package::
32
33 from MoinMoin._tests import run
34 run(request)
35
36 Running only few modules::
37
38 run(request, names=['test_this', 'test_that'])
39
40 @copyright: 2002-2004 by Juergen Hermann <jh@web.de>
41 @license: GNU GPL, see COPYING for details.
42 """
43
44 import sys
45
46 from unittest import TestLoader, TextTestRunner
47
48
49 class TestSkipped(Exception):
50 """ Raised when a tests is skipped """
51
52 TestSkiped = TestSkipped # ensure a stable interface
53
54 class TestConfig:
55 """ Custom configuration for unit tests
56
57 Some test assume specific configuration, and will fail if the wiki admin
58 will change the configuration. For example, DateTime macro test assume
59 the default datetime_fmt.
60
61 When you set custom values in a TestConfig, the previous values are saved,
62 and when the TestConfig is deleted, they are restored automatically.
63
64 Typical Usage
65 -------------
66 ::
67 from MoinMoin._tests import TestConfig
68 class SomeTestCase(unittest.TestCase):
69 def setUp(self):
70 self.config = TestConfig(self.request,
71 defaults=key_list, key=value,...)
72 def tearDown(self):
73 del self.config
74 def testSomething(self):
75 # test that needs those defaults and custom values
76 """
77
78 def __init__(self, request, defaults=(), **custom):
79 """ Create temporary configuration for a test
80
81 @param request: current request
82 @param defaults: list of keys that should use the default value
83 @param custom: other keys using non default values, or new keys
84 that request.cfg does not have already
85 """
86 self.request = request
87 self.old = {} # Old config values
88 self.new = [] # New added attributes
89 self.setDefaults(defaults)
90 self.setCustom(**custom)
91
92 def setDefaults(self, defaults=()):
93 """ Set default values for keys in defaults list
94
95 Non existing default will raise an AttributeError.
96 """
97 from MoinMoin.config import multiconfig
98 for key in defaults:
99 self._setattr(key, getattr(multiconfig.DefaultConfig, key))
100
101 def setCustom(self, **custom):
102 """ Set custom values """
103 for key, value in custom.items():
104 self._setattr(key, value)
105
106 def _setattr(self, key, value):
107 """ Set a new value for key saving new added keys """
108 if hasattr(self.request.cfg, key):
109 self.old[key] = getattr(self.request.cfg, key)
110 else:
111 self.new.append(key)
112 setattr(self.request.cfg, key, value)
113
114 def __del__(self):
115 """ Restore previous request.cfg
116
117 Set old keys to old values and delete new keys.
118 """
119 for key, value in self.old.items():
120 setattr(self.request.cfg, key, value)
121 for key in self.new:
122 delattr(self.request.cfg, key)
123
124
125 class MoinTestLoader(TestLoader):
126 """ Customized test loader that support long running process
127
128 To enable testing long running process, we inject the current
129 request into each test case class. Later, each test can refer to
130 request as self.request.
131 """
132 def __init__(self, request):
133 self.request = request
134
135 def loadTestsFromTestCase(self, testCaseClass):
136 testCaseClass.request = self.request
137 return TestLoader.loadTestsFromTestCase(self, testCaseClass)
138
139 def loadTestsFromModuleNames(self, names):
140 """ Load tests from qualified module names, eg. a.b.c
141
142 loadTestsFromNames is broken, hiding ImportErrros in test
143 modules. This method is less flexsible but works correctly.
144 """
145 names = ['%s.%s' % (__name__, name) for name in names]
146 suites = []
147 for name in names:
148 module = __import__(name, globals(), {}, ['dummy'])
149 suites.append(self.loadTestsFromModule(module))
150 return self.suiteClass(suites)
151
152
153 def makeSuite(request, names=None):
154 """ Create test suites from modules in names
155
156 @param request: current request
157 @param names: module names to get tests from. If the list is empty,
158 all test modules in the _tests package are used.
159 @rtype: C{unittest.TestSuite}
160 @return: test suite with all test cases in names
161 """
162 if not names:
163 from MoinMoin.util.pysupport import getPackageModules
164 names = getPackageModules(__file__)
165 names = [name for name in names if name.startswith('test_')]
166 caseInsensitiveCompare = lambda a, b: cmp(a.lower(), b.lower())
167 names.sort(caseInsensitiveCompare)
168
169 return MoinTestLoader(request).loadTestsFromModuleNames(names)
170
171
172 def run(request=None, names=None):
173 """ Run test suit
174
175 @param request: current request
176 @param names: list fully qualified module names to test,
177 e.g MoinMoin._tests.test_error
178 """
179 if request is None:
180 from MoinMoin.request import CLI
181 from MoinMoin.user import User
182 request = CLI.Request()
183 request.form = request.args = request.setup_args()
184 request.user = User(request)
185
186 suite = makeSuite(request, names)
187
188 # do not redirect the stream to request here because request.write can
189 # be invalid or broken because not all redirections of it use a finally: block
190 TextTestRunner(stream=sys.stdout, verbosity=2).run(suite)
191