Using Constants
This guide shows how to use the centralized constants in SegnoMMS to write more maintainable and reliable code, whether in tests, examples, or applications.
Why Use Constants?
Using string literals in tests creates several problems:
Brittleness: Typos in strings cause runtime failures
Inconsistency: Same value written differently in different places
No autocomplete: IDEs can’t help with string values
No validation: Invalid values only discovered at runtime
Refactoring difficulty: Changing values requires finding all occurrences
The Constants Module
The segnomms.constants module provides:
Enum re-exports for type safety
Predefined data sets for common use cases
Common default values
Utility functions for configuration
Migration Examples
Before: Using String Literals
# ❌ Brittle string literals
def test_circle_shape():
write(qr, output, shape="circle", scale=10)
# ❌ Typo causes runtime error
def test_squircle_shape():
write(qr, output, shape="squirle") # cspell:disable-line - intentional typo example
# ❌ Inconsistent test data
def test_colors():
test_cases = [
{"dark": "#000000", "light": "#FFFFFF"},
{"dark": "#000", "light": "#FFF"}, # Same color, different format
]
After: Using Constants
from segnomms.constants import ModuleShape, create_config, TEST_COLORS
# ✅ Type-safe enum values
def test_circle_shape():
config = create_config(shape=ModuleShape.CIRCLE.value)
write(qr, output, **config)
# ✅ IDE autocomplete prevents typos
def test_squircle_shape():
config = create_config(shape=ModuleShape.SQUIRCLE.value)
write(qr, output, **config)
# ✅ Consistent test data
def test_colors():
config = create_config(
dark=TEST_COLORS["black"],
light=TEST_COLORS["white"]
)
write(qr, output, **config)
Common Patterns
Testing All Shapes
from segnomms.constants import VALID_SHAPES
@pytest.mark.parametrize("shape", VALID_SHAPES)
def test_all_shapes(shape):
config = create_config(shape=shape)
write(qr, output, **config)
# Test assertions...
Testing Shape Categories
from segnomms.constants import BASIC_SHAPES, CONNECTED_SHAPES
def test_basic_shapes_only():
for shape in BASIC_SHAPES:
# Basic shapes should work with safe mode
config = create_config(shape=shape, safe_mode=True)
write(qr, output, **config)
def test_connected_shapes():
for shape in CONNECTED_SHAPES:
# Connected shapes need special handling
config = create_config(
shape=shape,
merge=MergeStrategy.SOFT.value,
connectivity=ConnectivityMode.EIGHT_WAY.value
)
write(qr, output, **config)
Using Predefined Test Cases
You can create your own test case dictionaries or iterate over available shapes:
from segnomms.constants import VALID_SHAPES, BASIC_SHAPES, create_config
# Define your own test cases using available constants
SHAPE_TEST_CASES = [
{"shape": shape, "corner_radius": 0.3}
for shape in BASIC_SHAPES
]
@pytest.mark.parametrize("test_case", SHAPE_TEST_CASES)
def test_shape_configurations(test_case):
config = create_config(
shape=test_case["shape"],
corner_radius=test_case["corner_radius"]
)
write(qr, output, **config)
Testing with Common Payloads
from segnomms.constants import QR_PAYLOADS
def test_url_encoding():
qr = segno.make(QR_PAYLOADS["url"])
# Test URL-specific behavior
def test_unicode_support():
qr = segno.make(QR_PAYLOADS["unicode"])
# Test Unicode handling
Best Practices
1. Import What You Need
# Import specific constants
from segnomms.constants import ModuleShape, DEFAULT_SCALE
# Or import categories
from segnomms.constants import VALID_SHAPES, TEST_COLORS
2. Use Enum Values
# ✅ Good: Use enum value
shape = ModuleShape.CIRCLE.value
# ❌ Avoid: Direct string
shape = "circle"
3. Extend Constants Module
When adding new test cases, add them to the constants module:
# In your test file, define test cases using existing constants
from segnomms.constants import VALID_SHAPES, TEST_COLORS, create_config
FEATURE_TEST_CASES = [
{"shape": shape, "dark": TEST_COLORS["black"]}
for shape in VALID_SHAPES[:3] # Test first few shapes
]
@pytest.mark.parametrize("test_case", FEATURE_TEST_CASES)
def test_feature(test_case):
config = create_config(**test_case)
# ... test implementation
4. Type Hints
Use type hints with enums for better IDE support:
from segnomms.constants import ModuleShape
def create_qr_with_shape(shape: str) -> str:
# shape should be ModuleShape.XXX.value
config = create_config(shape=shape)
# ...
Available Constants
Shapes
from segnomms.constants import (
VALID_SHAPES, # All valid shape names
BASIC_SHAPES, # Simple geometric shapes
CONNECTED_SHAPES, # Shapes that support merging
FINDER_SHAPES # Finder pattern shapes
)
Colors
from segnomms.constants import (
TEST_COLORS, # Named color constants for testing
DEFAULT_DARK, # Standard dark color
DEFAULT_LIGHT # Standard light color
)
Payloads
from segnomms.constants import (
QR_PAYLOADS # Common test content (url, simple, unicode, etc.)
)
Configuration Helpers
from segnomms.constants import (
create_config, # Build consistent configs
DEFAULT_SCALE, # Standard scale value (10)
DEFAULT_BORDER # Standard border size (4)
)
Test Helpers
SegnoMMS also provides test helper classes for advanced testing scenarios:
from tests.helpers import (
QRScanabilityHarness, # QR code scanning validation
TestCaseGenerator, # Generate standardized test cases
TestOutputManager # Organized file output management
)
QRScanabilityHarness - Validates that generated QR codes are actually scannable:
from tests.helpers import get_scanability_harness
def test_qr_scanability():
qr = segno.make("Test content")
config = create_config(shape=ModuleShape.CIRCLE.value)
# Generate SVG
output = StringIO()
write(qr, output, **config)
svg_content = output.getvalue()
# Validate scanability
harness = get_scanability_harness()
result = harness.validate_svg(svg_content, "Test content")
assert result.is_scannable
TestCaseGenerator - Creates standardized test case configurations:
from tests.helpers import TestCaseGenerator, TestCategory
def test_shape_gallery():
# Get all shape test cases
test_cases = TestCaseGenerator.get_shape_test_cases()
for test_case in test_cases:
qr = TestCaseGenerator.generate_qr(test_case)
# Test each shape configuration...
TestOutputManager - Organizes test output files systematically:
from tests.helpers import TestOutputManager
def test_visual_regression():
output_manager = TestOutputManager(Path("test_output"))
# Generate organized output with SVG, PNG, and config JSON
outputs = output_manager.generate_test_output(
test_case_id="shape_circle_safe_mode",
qr_code=qr,
config=config,
output_type="regression"
)
Benefits
Maintainability: Change values in one place
Reliability: Catch errors at import time
Discoverability: See all valid values easily
Documentation: Constants serve as living documentation
Consistency: Same values used everywhere
IDE Support: Autocomplete and type checking
Migration Checklist
When updating existing tests:
Replace string literals with enum values
Use predefined test data sets
Replace magic numbers with named constants
Use
create_config()for consistencyAdd new test cases to constants module
Remove duplicate test data definitions
Add type hints where helpful
Example Migration
Before
def test_various_shapes():
# ❌ String literals, inconsistent configs
for shape in ["square", "circle", "rounded"]:
write(qr, output, shape=shape, scale=10, border=4)
# ❌ Magic numbers
write(qr, output, shape="squircle", corner_radius=0.3, scale=8)
# ❌ Duplicate color definitions
write(qr, output, dark="#000000", light="#FFFFFF")
After
from segnomms.constants import (
BASIC_SHAPES,
create_config,
ModuleShape,
TEST_COLORS
)
def test_various_shapes():
# ✅ Type-safe shapes, consistent config
for shape in BASIC_SHAPES:
config = create_config(shape=shape)
write(qr, output, **config)
# ✅ Named constants
squircle_config = create_config(
shape=ModuleShape.SQUIRCLE.value,
corner_radius=0.3
)
write(qr, output, **squircle_config)
# ✅ Predefined test data
color_config = create_config(
dark=TEST_COLORS["brand_primary"],
light=TEST_COLORS["brand_secondary"]
)
write(qr, output, **color_config)
This approach makes tests more maintainable, reduces errors, and provides better developer experience through IDE support and type safety.