0
# Testing Utilities
1
2
Utilities for writing tests that work across Python versions, including assertion method compatibility. These functions provide unified interfaces for test assertions that handle the method name differences between Python 2 and 3 unittest modules.
3
4
## Capabilities
5
6
### Assertion Method Compatibility
7
8
Functions that provide consistent assertion methods across Python versions by wrapping the appropriate unittest methods.
9
10
```python { .api }
11
def assertCountEqual(self, *args, **kwargs) -> None
12
"""Assert that two iterables have the same elements regardless of order."""
13
14
def assertRaisesRegex(self, *args, **kwargs) -> ContextManager
15
"""Assert that an exception is raised and its message matches a regex pattern."""
16
17
def assertRegex(self, *args, **kwargs) -> None
18
"""Assert that a string matches a regular expression pattern."""
19
20
def assertNotRegex(self, *args, **kwargs) -> None
21
"""Assert that a string does not match a regular expression pattern."""
22
```
23
24
**Note:** These functions are wrappers that delegate to the appropriate unittest method based on Python version, using the correct method names (`assertItemsEqual`/`assertCountEqual`, `assertRaisesRegexp`/`assertRaisesRegex`, etc.). They accept the same arguments as the underlying unittest methods.
25
26
**Usage Examples:**
27
28
```python
29
import six
30
import unittest
31
import re
32
33
class MyTestCase(unittest.TestCase):
34
35
def test_count_equal_assertion(self):
36
"""Test assertCountEqual functionality."""
37
list1 = [1, 2, 3, 2]
38
list2 = [2, 1, 3, 2]
39
40
# Use six.assertCountEqual for cross-version compatibility
41
six.assertCountEqual(self, list1, list2)
42
43
# This assertion would fail
44
with self.assertRaises(AssertionError):
45
six.assertCountEqual(self, [1, 2, 3], [1, 2, 3, 4])
46
47
def test_regex_assertions(self):
48
"""Test regex-based assertions."""
49
text = "Hello, World! This is a test message."
50
51
# Assert text matches pattern
52
six.assertRegex(self, text, r"Hello.*World")
53
six.assertRegex(self, text, re.compile(r"\w+\s+\w+"))
54
55
# Assert text does not match pattern
56
six.assertNotRegex(self, text, r"goodbye")
57
six.assertNotRegex(self, text, re.compile(r"\d+"))
58
59
def test_exception_with_regex(self):
60
"""Test exception raising with regex matching."""
61
62
def problematic_function():
63
raise ValueError("Invalid input: expected number, got string")
64
65
# Assert exception is raised with matching message
66
with six.assertRaisesRegex(self, ValueError, r"Invalid input.*expected"):
67
problematic_function()
68
69
# Using compiled regex
70
pattern = re.compile(r"expected \w+, got \w+")
71
with six.assertRaisesRegex(self, ValueError, pattern):
72
problematic_function()
73
74
if __name__ == '__main__':
75
unittest.main()
76
```
77
78
## Assertion Method Constants
79
80
String constants that provide the correct method names for different Python versions.
81
82
```python { .api }
83
_assertCountEqual: str # Method name for count equal assertion
84
_assertRaisesRegex: str # Method name for raises regex assertion
85
_assertRegex: str # Method name for regex assertion
86
_assertNotRegex: str # Method name for not regex assertion
87
```
88
89
These constants contain the appropriate method names:
90
- Python 2: `assertItemsEqual`, `assertRaisesRegexp`, `assertRegexpMatches`, `assertNotRegexpMatches`
91
- Python 3: `assertCountEqual`, `assertRaisesRegex`, `assertRegex`, `assertNotRegex`
92
93
**Usage Example:**
94
95
```python
96
import six
97
import unittest
98
99
class MyTestCase(unittest.TestCase):
100
101
def test_using_constants(self):
102
"""Example of using method name constants directly."""
103
104
# Get the appropriate method for the current Python version
105
count_equal_method = getattr(self, six._assertCountEqual)
106
107
# Use the method
108
count_equal_method([1, 2, 3], [3, 2, 1])
109
110
# Similarly for regex assertions
111
regex_method = getattr(self, six._assertRegex)
112
regex_method("hello world", r"hello.*world")
113
```
114
115
## Advanced Testing Patterns
116
117
### Custom Test Base Class
118
119
```python
120
import six
121
import unittest
122
123
class CrossVersionTestCase(unittest.TestCase):
124
"""Base test case with cross-version assertion helpers."""
125
126
def assertCountEqual(self, first, second, msg=None):
127
"""Wrapper for assertCountEqual that works across versions."""
128
return six.assertCountEqual(self, first, second, msg)
129
130
def assertRaisesRegex(self, expected_exception, expected_regex):
131
"""Wrapper for assertRaisesRegex that works across versions."""
132
return six.assertRaisesRegex(self, expected_exception, expected_regex)
133
134
def assertRegex(self, text, expected_regex, msg=None):
135
"""Wrapper for assertRegex that works across versions."""
136
return six.assertRegex(self, text, expected_regex, msg)
137
138
def assertNotRegex(self, text, unexpected_regex, msg=None):
139
"""Wrapper for assertNotRegex that works across versions."""
140
return six.assertNotRegex(self, text, unexpected_regex, msg)
141
142
def assertStringTypes(self, value, msg=None):
143
"""Assert that value is a string type (works across versions)."""
144
self.assertIsInstance(value, six.string_types, msg)
145
146
def assertTextType(self, value, msg=None):
147
"""Assert that value is a text type (unicode/str)."""
148
self.assertIsInstance(value, six.text_type, msg)
149
150
def assertBinaryType(self, value, msg=None):
151
"""Assert that value is a binary type (bytes/str)."""
152
self.assertIsInstance(value, six.binary_type, msg)
153
154
# Usage example
155
class StringProcessorTest(CrossVersionTestCase):
156
157
def test_string_processing(self):
158
"""Test string processing functionality."""
159
from mymodule import process_string
160
161
result = process_string("hello")
162
163
# Use cross-version assertions
164
self.assertStringTypes(result)
165
self.assertRegex(result, r"^processed:")
166
167
# Test with unicode
168
unicode_input = six.u("héllo")
169
unicode_result = process_string(unicode_input)
170
self.assertTextType(unicode_result)
171
```
172
173
### Parameterized Testing with Version Checks
174
175
```python
176
import six
177
import unittest
178
179
class VersionAwareTest(unittest.TestCase):
180
"""Test class that adapts behavior based on Python version."""
181
182
def setUp(self):
183
"""Set up test fixtures based on Python version."""
184
if six.PY2:
185
self.string_type = unicode
186
self.binary_type = str
187
else:
188
self.string_type = str
189
self.binary_type = bytes
190
191
def test_string_handling(self):
192
"""Test that varies behavior by Python version."""
193
test_data = [
194
("hello", "HELLO"),
195
(six.u("wörld"), six.u("WÖRLD")),
196
]
197
198
for input_val, expected in test_data:
199
with self.subTest(input=input_val):
200
result = input_val.upper()
201
self.assertEqual(result, expected)
202
self.assertIsInstance(result, self.string_type)
203
204
@unittest.skipUnless(six.PY3, "Python 3 only test")
205
def test_python3_feature(self):
206
"""Test that only runs on Python 3."""
207
# Test Python 3 specific functionality
208
self.assertRegex("test string", r"test.*string")
209
210
@unittest.skipUnless(six.PY2, "Python 2 only test")
211
def test_python2_feature(self):
212
"""Test that only runs on Python 2."""
213
# Test Python 2 specific functionality
214
self.assertRegexpMatches("test string", r"test.*string")
215
216
# Decorator for version-specific tests
217
def py2_only(func):
218
"""Decorator to skip test on Python 3."""
219
return unittest.skipUnless(six.PY2, "Python 2 only")(func)
220
221
def py3_only(func):
222
"""Decorator to skip test on Python 2."""
223
return unittest.skipUnless(six.PY3, "Python 3 only")(func)
224
225
class MyFeatureTest(unittest.TestCase):
226
227
@py2_only
228
def test_legacy_behavior(self):
229
"""Test legacy behavior specific to Python 2."""
230
pass
231
232
@py3_only
233
def test_modern_behavior(self):
234
"""Test modern behavior specific to Python 3."""
235
pass
236
```
237
238
### Mock and Patch Utilities
239
240
```python
241
import six
242
import unittest
243
244
try:
245
from unittest.mock import Mock, patch, MagicMock
246
except ImportError:
247
# Python 2 fallback
248
from mock import Mock, patch, MagicMock
249
250
class MockingTest(unittest.TestCase):
251
"""Example of version-aware mocking."""
252
253
def test_mocked_function(self):
254
"""Test with mocked function using six utilities."""
255
256
with patch('mymodule.some_function') as mock_func:
257
mock_func.return_value = six.u("mocked result")
258
259
from mymodule import call_some_function
260
result = call_some_function()
261
262
# Verify the result using six assertions
263
six.assertRegex(self, result, r"mocked.*result")
264
mock_func.assert_called_once()
265
266
def test_string_type_handling(self):
267
"""Test mocking with proper string type handling."""
268
269
mock_obj = Mock()
270
mock_obj.get_data.return_value = six.ensure_text("test data")
271
272
result = mock_obj.get_data()
273
274
# Verify result is proper text type
275
self.assertIsInstance(result, six.text_type)
276
six.assertRegex(self, result, r"test.*data")
277
278
# Test runner helper
279
def run_cross_version_tests():
280
"""Helper to run tests with version information."""
281
import sys
282
283
six.print_(f"Running tests on Python {sys.version}")
284
six.print_(f"Six version: {six.__version__}")
285
six.print_(f"PY2: {six.PY2}, PY3: {six.PY3}")
286
287
# Discover and run tests
288
loader = unittest.TestLoader()
289
suite = loader.discover('.')
290
runner = unittest.TextTestRunner(verbosity=2)
291
result = runner.run(suite)
292
293
return result.wasSuccessful()
294
295
if __name__ == '__main__':
296
success = run_cross_version_tests()
297
sys.exit(0 if success else 1)
298
```
299
300
## Integration with Testing Frameworks
301
302
### pytest Integration
303
304
```python
305
import six
306
import pytest
307
308
class TestSixIntegration:
309
"""pytest tests using six utilities."""
310
311
def test_assertion_methods(self):
312
"""Test six assertion methods with pytest."""
313
314
# Note: six assertion methods expect unittest.TestCase self
315
# Create a minimal test case instance
316
import unittest
317
test_case = unittest.TestCase()
318
319
# Use six assertions
320
six.assertCountEqual(test_case, [1, 2, 3], [3, 2, 1])
321
six.assertRegex(test_case, "hello world", r"hello.*world")
322
323
with pytest.raises(AssertionError):
324
six.assertCountEqual(test_case, [1, 2], [1, 2, 3])
325
326
@pytest.mark.skipif(not six.PY3, reason="Python 3 only")
327
def test_py3_specific(self):
328
"""Test that only runs on Python 3."""
329
assert six.PY3
330
assert not six.PY2
331
332
def test_string_types(self):
333
"""Test string type checking with pytest."""
334
test_string = "hello"
335
assert isinstance(test_string, six.string_types)
336
337
if six.PY2:
338
unicode_string = six.u("hello")
339
assert isinstance(unicode_string, six.text_type)
340
```
341
342
This comprehensive testing utilities documentation provides developers with all the tools they need to write cross-version compatible tests using the six library.