Thyme
Testing

Testing Reference

MockContext - unit test pipelines and extractors without infrastructure.

MockContext is an in-memory pipeline simulator that mirrors the engine's aggregation semantics. It lets you unit test datasets, pipelines, and extractors without running Postgres, Kafka, or any services.

Quick start

from thyme.testing import MockContext

ctx = MockContext()

# Ingest events
violations = ctx.add_events(Review, [
    {"restaurant_id": "r1", "rating": 4.8, "timestamp": "2026-03-15T10:00:00Z"},
    {"restaurant_id": "r1", "rating": 4.2, "timestamp": "2026-03-15T11:00:00Z"},
])

# Check raw aggregates
result = ctx.get_aggregates(RestaurantRatingStats, "r1")
assert result["avg_rating_7d"] == 4.5

# Query features through extractors
features = ctx.query(RestaurantFeatures, "r1")
assert features["avg_rating_7d"] == 4.5

API

MockContext()

Creates a new in-memory context. No arguments.

ctx = MockContext()

ctx.add_events(dataset_class, events) -> list[ExpectationViolation]

Ingests events for a dataset and processes them through all registered pipelines.

ParameterTypeDescription
dataset_classtypeThe @dataset-decorated class
eventslist[dict]List of event dicts matching the dataset schema

Returns: A list of ExpectationViolation objects (empty if no expectations are defined or all pass). Events are still processed even when violations occur - expectations are observational.

Event format: Each event is a dict with keys matching the dataset field names. Timestamps can be datetime objects, ISO 8601 strings, or numeric epoch seconds.

violations = ctx.add_events(Order, [
    {"user_id": "u1", "amount": 42.50, "timestamp": "2026-03-15T10:00:00Z"},
    {"user_id": "u1", "amount": 15.00, "timestamp": "2026-03-15T11:00:00Z"},
])

ctx.get_aggregates(dataset_class, entity_id) -> dict[str, Any]

Returns raw aggregated values for an entity from all pipelines on the dataset.

ParameterTypeDescription
dataset_classtypeThe output @dataset class (where the pipeline is defined)
entity_idstrThe entity key value

Returns: A {output_field: value} dict. Missing entities return zeros.

result = ctx.get_aggregates(UserOrderStats, "u1")
# {"order_count_1h": 2.0, "total_spend_24h": 57.5, ...}

ctx.query(featureset_class, entity_id) -> dict[str, Any]

Queries features for an entity, running extractors in dependency order.

ParameterTypeDescription
featureset_classtypeThe @featureset-decorated class
entity_idstrThe entity key value

Returns: A {feature_name: value} dict with all computed features.

features = ctx.query(FraudSignals, "u_fraud")
# {"user_id": "u_fraud", "order_count_1h": 6, "is_suspicious": True, ...}

The query method:

  1. Seeds features from pipeline aggregates (for extractors with deps)
  2. Runs pure-transform extractors (no deps) with the seeded values

Full example

from thyme.testing import MockContext

def test_fraud_detection():
    # Given: a mock context with events
    ctx = MockContext()
    ctx.add_events(Order, [
        {"user_id": "u1", "order_id": "o1", "amount": 100.0,
         "item_count": 2, "timestamp": "2026-03-15T10:00:00Z"},
        {"user_id": "u1", "order_id": "o2", "amount": 200.0,
         "item_count": 1, "timestamp": "2026-03-15T10:05:00Z"},
    ])

    # When: querying aggregates
    stats = ctx.get_aggregates(UserOrderStats, "u1")

    # Then: aggregations are correct
    assert stats["order_count_1h"] == 2.0
    assert stats["total_spend_24h"] == 300.0

    # And: features are computed through extractors
    features = ctx.query(FraudSignals, "u1")
    assert features["is_suspicious"] == False

Supported aggregations

MockContext supports all 6 aggregation operators:

OperatorMockContext behavior
CountCounts events in window
SumSums field values in window
AvgMean of field values in window (0.0 if empty)
MinMinimum field value in window
MaxMaximum field value in window
ApproxPercentileExact percentile rank (not approximate - good enough for tests)

The ApproxPercentile operator uses exact percentile rank computation in tests, which is deterministic and sufficient for correctness verification.

On this page