CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-six

Python 2 and 3 compatibility utilities

Pending
Overview
Eval results
Files

testing.mddocs/

Testing Utilities

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.

Capabilities

Assertion Method Compatibility

Functions that provide consistent assertion methods across Python versions by wrapping the appropriate unittest methods.

def assertCountEqual(self, *args, **kwargs) -> None
    """Assert that two iterables have the same elements regardless of order."""

def assertRaisesRegex(self, *args, **kwargs) -> ContextManager
    """Assert that an exception is raised and its message matches a regex pattern."""

def assertRegex(self, *args, **kwargs) -> None
    """Assert that a string matches a regular expression pattern."""

def assertNotRegex(self, *args, **kwargs) -> None
    """Assert that a string does not match a regular expression pattern."""

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.

Usage Examples:

import six
import unittest
import re

class MyTestCase(unittest.TestCase):
    
    def test_count_equal_assertion(self):
        """Test assertCountEqual functionality."""
        list1 = [1, 2, 3, 2]
        list2 = [2, 1, 3, 2]
        
        # Use six.assertCountEqual for cross-version compatibility
        six.assertCountEqual(self, list1, list2)
        
        # This assertion would fail
        with self.assertRaises(AssertionError):
            six.assertCountEqual(self, [1, 2, 3], [1, 2, 3, 4])
    
    def test_regex_assertions(self):
        """Test regex-based assertions."""
        text = "Hello, World! This is a test message."
        
        # Assert text matches pattern
        six.assertRegex(self, text, r"Hello.*World")
        six.assertRegex(self, text, re.compile(r"\w+\s+\w+"))
        
        # Assert text does not match pattern
        six.assertNotRegex(self, text, r"goodbye")
        six.assertNotRegex(self, text, re.compile(r"\d+"))
    
    def test_exception_with_regex(self):
        """Test exception raising with regex matching."""
        
        def problematic_function():
            raise ValueError("Invalid input: expected number, got string")
        
        # Assert exception is raised with matching message
        with six.assertRaisesRegex(self, ValueError, r"Invalid input.*expected"):
            problematic_function()
        
        # Using compiled regex
        pattern = re.compile(r"expected \w+, got \w+")
        with six.assertRaisesRegex(self, ValueError, pattern):
            problematic_function()

if __name__ == '__main__':
    unittest.main()

Assertion Method Constants

String constants that provide the correct method names for different Python versions.

_assertCountEqual: str     # Method name for count equal assertion
_assertRaisesRegex: str    # Method name for raises regex assertion  
_assertRegex: str          # Method name for regex assertion
_assertNotRegex: str       # Method name for not regex assertion

These constants contain the appropriate method names:

  • Python 2: assertItemsEqual, assertRaisesRegexp, assertRegexpMatches, assertNotRegexpMatches
  • Python 3: assertCountEqual, assertRaisesRegex, assertRegex, assertNotRegex

Usage Example:

import six
import unittest

class MyTestCase(unittest.TestCase):
    
    def test_using_constants(self):
        """Example of using method name constants directly."""
        
        # Get the appropriate method for the current Python version
        count_equal_method = getattr(self, six._assertCountEqual)
        
        # Use the method
        count_equal_method([1, 2, 3], [3, 2, 1])
        
        # Similarly for regex assertions
        regex_method = getattr(self, six._assertRegex)
        regex_method("hello world", r"hello.*world")

Advanced Testing Patterns

Custom Test Base Class

import six
import unittest

class CrossVersionTestCase(unittest.TestCase):
    """Base test case with cross-version assertion helpers."""
    
    def assertCountEqual(self, first, second, msg=None):
        """Wrapper for assertCountEqual that works across versions."""
        return six.assertCountEqual(self, first, second, msg)
    
    def assertRaisesRegex(self, expected_exception, expected_regex):
        """Wrapper for assertRaisesRegex that works across versions."""
        return six.assertRaisesRegex(self, expected_exception, expected_regex)
    
    def assertRegex(self, text, expected_regex, msg=None):
        """Wrapper for assertRegex that works across versions."""
        return six.assertRegex(self, text, expected_regex, msg)
    
    def assertNotRegex(self, text, unexpected_regex, msg=None):
        """Wrapper for assertNotRegex that works across versions."""
        return six.assertNotRegex(self, text, unexpected_regex, msg)
    
    def assertStringTypes(self, value, msg=None):
        """Assert that value is a string type (works across versions)."""
        self.assertIsInstance(value, six.string_types, msg)
    
    def assertTextType(self, value, msg=None):
        """Assert that value is a text type (unicode/str)."""
        self.assertIsInstance(value, six.text_type, msg)
    
    def assertBinaryType(self, value, msg=None):
        """Assert that value is a binary type (bytes/str)."""
        self.assertIsInstance(value, six.binary_type, msg)

# Usage example
class StringProcessorTest(CrossVersionTestCase):
    
    def test_string_processing(self):
        """Test string processing functionality."""
        from mymodule import process_string
        
        result = process_string("hello")
        
        # Use cross-version assertions
        self.assertStringTypes(result)
        self.assertRegex(result, r"^processed:")
        
        # Test with unicode
        unicode_input = six.u("héllo")
        unicode_result = process_string(unicode_input)
        self.assertTextType(unicode_result)

Parameterized Testing with Version Checks

import six
import unittest

class VersionAwareTest(unittest.TestCase):
    """Test class that adapts behavior based on Python version."""
    
    def setUp(self):
        """Set up test fixtures based on Python version."""
        if six.PY2:
            self.string_type = unicode
            self.binary_type = str
        else:
            self.string_type = str
            self.binary_type = bytes
    
    def test_string_handling(self):
        """Test that varies behavior by Python version."""
        test_data = [
            ("hello", "HELLO"),
            (six.u("wörld"), six.u("WÖRLD")),
        ]
        
        for input_val, expected in test_data:
            with self.subTest(input=input_val):
                result = input_val.upper()
                self.assertEqual(result, expected)
                self.assertIsInstance(result, self.string_type)
    
    @unittest.skipUnless(six.PY3, "Python 3 only test")
    def test_python3_feature(self):
        """Test that only runs on Python 3."""
        # Test Python 3 specific functionality
        self.assertRegex("test string", r"test.*string")
    
    @unittest.skipUnless(six.PY2, "Python 2 only test")
    def test_python2_feature(self):
        """Test that only runs on Python 2."""
        # Test Python 2 specific functionality
        self.assertRegexpMatches("test string", r"test.*string")

# Decorator for version-specific tests
def py2_only(func):
    """Decorator to skip test on Python 3."""
    return unittest.skipUnless(six.PY2, "Python 2 only")(func)

def py3_only(func):
    """Decorator to skip test on Python 2."""
    return unittest.skipUnless(six.PY3, "Python 3 only")(func)

class MyFeatureTest(unittest.TestCase):
    
    @py2_only
    def test_legacy_behavior(self):
        """Test legacy behavior specific to Python 2."""
        pass
    
    @py3_only  
    def test_modern_behavior(self):
        """Test modern behavior specific to Python 3."""
        pass

Mock and Patch Utilities

import six
import unittest

try:
    from unittest.mock import Mock, patch, MagicMock
except ImportError:
    # Python 2 fallback
    from mock import Mock, patch, MagicMock

class MockingTest(unittest.TestCase):
    """Example of version-aware mocking."""
    
    def test_mocked_function(self):
        """Test with mocked function using six utilities."""
        
        with patch('mymodule.some_function') as mock_func:
            mock_func.return_value = six.u("mocked result")
            
            from mymodule import call_some_function
            result = call_some_function()
            
            # Verify the result using six assertions
            six.assertRegex(self, result, r"mocked.*result")
            mock_func.assert_called_once()
    
    def test_string_type_handling(self):
        """Test mocking with proper string type handling."""
        
        mock_obj = Mock()
        mock_obj.get_data.return_value = six.ensure_text("test data")
        
        result = mock_obj.get_data()
        
        # Verify result is proper text type
        self.assertIsInstance(result, six.text_type)
        six.assertRegex(self, result, r"test.*data")

# Test runner helper
def run_cross_version_tests():
    """Helper to run tests with version information."""
    import sys
    
    six.print_(f"Running tests on Python {sys.version}")
    six.print_(f"Six version: {six.__version__}")
    six.print_(f"PY2: {six.PY2}, PY3: {six.PY3}")
    
    # Discover and run tests
    loader = unittest.TestLoader()
    suite = loader.discover('.')
    runner = unittest.TextTestRunner(verbosity=2)
    result = runner.run(suite)
    
    return result.wasSuccessful()

if __name__ == '__main__':
    success = run_cross_version_tests()
    sys.exit(0 if success else 1)

Integration with Testing Frameworks

pytest Integration

import six
import pytest

class TestSixIntegration:
    """pytest tests using six utilities."""
    
    def test_assertion_methods(self):
        """Test six assertion methods with pytest."""
        
        # Note: six assertion methods expect unittest.TestCase self
        # Create a minimal test case instance
        import unittest
        test_case = unittest.TestCase()
        
        # Use six assertions
        six.assertCountEqual(test_case, [1, 2, 3], [3, 2, 1])
        six.assertRegex(test_case, "hello world", r"hello.*world")
        
        with pytest.raises(AssertionError):
            six.assertCountEqual(test_case, [1, 2], [1, 2, 3])
    
    @pytest.mark.skipif(not six.PY3, reason="Python 3 only")
    def test_py3_specific(self):
        """Test that only runs on Python 3."""
        assert six.PY3
        assert not six.PY2
    
    def test_string_types(self):
        """Test string type checking with pytest."""
        test_string = "hello"
        assert isinstance(test_string, six.string_types)
        
        if six.PY2:
            unicode_string = six.u("hello")
            assert isinstance(unicode_string, six.text_type)

This comprehensive testing utilities documentation provides developers with all the tools they need to write cross-version compatible tests using the six library.

Install with Tessl CLI

npx tessl i tessl/pypi-six

docs

execution.md

index.md

iterator-dict.md

metaclass.md

moves.md

string-bytes.md

testing.md

version-detection.md

tile.json