Testing Strategies and Best Practices

This document outlines recommended testing approaches for Semantiva components, with particular emphasis on avoiding internal API usage and following framework patterns.

Overview

Semantiva provides multiple approaches for testing components. The choice of testing strategy depends on the component type and testing goals:

  1. Pipeline-based testing (Recommended) - Tests components through pipeline execution

  2. Node-based testing (Conditional) - Direct processor testing with proper observers

  3. Unit testing (Limited scope) - Testing isolated utility functions

Warning

Avoid direct instantiation of internal APIs like _ContextObserver or _ValidatingContextObserver in tests. These are framework internals and their usage patterns may change.

Node-Based Testing (Limited Use)

Node-based testing involves direct processor instantiation and should only be used when:

  1. Pipeline testing is not feasible due to complex setup requirements

  2. Testing very specific processor behaviors in isolation

  3. Performance testing where pipeline overhead is problematic

Warning

When using node-based testing, never directly instantiate _ContextObserver or other internal framework APIs. Use the proper node execution infrastructure.

Correct Node-Based Testing Pattern

def test_processor_node_correctly():
    """Correct approach for node-based testing."""
    from semantiva.pipeline.node import Node
    from semantiva.pipeline.payload import Payload
    from semantiva.context_processors.context_types import ContextType

    # Create node properly
    node = Node(
        processor_name="YourProcessor",
        parameters={
            "param1": "value1",
            "param2": "value2"
        }
    )

    # Process with proper infrastructure
    initial_payload = Payload(data=test_data, context=ContextType())
    result_payload = node.process(initial_payload)

    # Check results in context
    result = result_payload.context.get_value("your.result.key")
    assert result == expected_value

Antipatterns to Avoid

The following patterns should be avoided in tests:

Antipattern 1: Direct Observer Instantiation

# ❌ DON'T DO THIS
def test_processor_wrong():
    processor = YourProcessor()
    context = ContextType()
    observer = _ContextObserver(context)  # Internal API!

    processor.operate_context_observer(observer, parameters)

    # This creates confusion about where to check results
    result = observer.observer_context.get_value("key")  # ❌
    # vs
    result = context.get_value("key")  # ❌ Which one?

Antipattern 2: Testing Against Internal State

# ❌ DON'T DO THIS
def test_processor_internal_state():
    processor = YourProcessor()
    processor._internal_method()  # Testing private methods
    assert processor._internal_state == expected  # Testing private state

Antipattern 3: Mocking Framework Infrastructure

# ❌ DON'T DO THIS
def test_with_excessive_mocking():
    with patch('semantiva.pipeline.node.Node'):
        with patch('semantiva.context_processors.observer._ContextObserver'):
            # Over-mocking breaks the framework contract
            test_logic()