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:

  1. Brittleness: Typos in strings cause runtime failures

  2. Inconsistency: Same value written differently in different places

  3. No autocomplete: IDEs can’t help with string values

  4. No validation: Invalid values only discovered at runtime

  5. 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

  1. Maintainability: Change values in one place

  2. Reliability: Catch errors at import time

  3. Discoverability: See all valid values easily

  4. Documentation: Constants serve as living documentation

  5. Consistency: Same values used everywhere

  6. 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 consistency

  • Add 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.