CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-io-opentelemetry--opentelemetry-sdk-testing

OpenTelemetry SDK Testing utilities providing comprehensive testing support for OpenTelemetry Java SDK applications with AssertJ assertions, in-memory exporters, and JUnit integration.

Pending
Overview
Eval results
Files

test-builders.mddocs/

Test Data Builders

Builder classes for creating test instances of OpenTelemetry telemetry data with full control over all properties and metadata. These builders enable creation of SpanData, MetricData, and LogRecordData objects for use in unit tests and test fixtures.

Capabilities

Span Data Builder

Builder for creating test SpanData instances with complete control over all span properties.

abstract class TestSpanData implements SpanData {
    // Factory method
    static Builder builder();
    
    static class Builder {
        // Identity and context
        Builder setSpanContext(SpanContext context);
        Builder setParentSpanContext(SpanContext parentContext);
        Builder setResource(Resource resource);
        Builder setInstrumentationScopeInfo(InstrumentationScopeInfo instrumentationScopeInfo);
        
        // Basic properties
        Builder setName(String name);
        Builder setKind(SpanKind kind);
        Builder setStartEpochNanos(long startEpochNanos);
        Builder setEndEpochNanos(long endEpochNanos);
        
        // Data and metadata
        Builder setAttributes(Attributes attributes);
        Builder setEvents(List<EventData> events);
        Builder setLinks(List<LinkData> links);
        Builder setStatus(StatusData status);
        
        // State and counts
        Builder setHasEnded(boolean hasEnded);
        Builder setTotalRecordedEvents(int totalRecordedEvents);
        Builder setTotalRecordedLinks(int totalRecordedLinks);
        Builder setTotalAttributeCount(int totalAttributeCount);
        
        // Build method
        TestSpanData build();
    }
}

Usage examples:

// Create a basic test span
TestSpanData span = TestSpanData.builder()
    .setName("test-operation")
    .setKind(SpanKind.INTERNAL)
    .setStartEpochNanos(TimeUnit.MILLISECONDS.toNanos(System.currentTimeMillis()))
    .setEndEpochNanos(TimeUnit.MILLISECONDS.toNanos(System.currentTimeMillis() + 100))
    .setAttributes(Attributes.of(
        AttributeKey.stringKey("service.name"), "test-service",
        AttributeKey.longKey("operation.id"), 12345L
    ))
    .setHasEnded(true)
    .build();

// Create a span with custom context
SpanContext spanContext = SpanContext.create(
    "12345678901234567890123456789012", 
    "1234567890123456", 
    TraceFlags.getSampled(), 
    TraceState.getDefault()
);

TestSpanData spanWithContext = TestSpanData.builder()
    .setSpanContext(spanContext)
    .setName("custom-context-span")
    .setStartEpochNanos(1000000000L)
    .setEndEpochNanos(2000000000L)
    .build();

// Create a span with events and links
List<EventData> events = Arrays.asList(
    EventData.create(1500000000L, "request.start"),
    EventData.create(1800000000L, "request.end", 
        Attributes.of(AttributeKey.longKey("response.size"), 1024L))
);

List<LinkData> links = Arrays.asList(
    LinkData.create(SpanContext.create("other-trace-id", "other-span-id", 
        TraceFlags.getDefault(), TraceState.getDefault()))
);

TestSpanData complexSpan = TestSpanData.builder()
    .setName("complex-operation")
    .setEvents(events)
    .setLinks(links)
    .setStatus(StatusData.create(StatusCode.OK, "Operation completed"))
    .setTotalRecordedEvents(2)
    .setTotalRecordedLinks(1)
    .build();

Metric Data Builder

Builder for creating test MetricData instances supporting all metric types (gauges, sums, histograms, exponential histograms, summaries).

abstract class TestMetricData implements MetricData {
    // Factory method
    static Builder builder();
    
    static class Builder {
        // Basic properties
        Builder setResource(Resource resource);
        Builder setInstrumentationScopeInfo(InstrumentationScopeInfo instrumentationScopeInfo);
        Builder setName(String name);
        Builder setDescription(String description);
        Builder setUnit(String unit);
        
        // Data type specific setters
        Builder setDoubleGaugeData(GaugeData<DoublePointData> data);
        Builder setLongGaugeData(GaugeData<LongPointData> data);
        Builder setDoubleSumData(SumData<DoublePointData> data);
        Builder setLongSumData(SumData<LongPointData> data);
        Builder setHistogramData(HistogramData data);
        Builder setExponentialHistogramData(ExponentialHistogramData data);
        Builder setSummaryData(SummaryData data);
        
        // Build method
        TestMetricData build();
    }
}

Usage examples:

// Create a double gauge metric
GaugeData<DoublePointData> gaugeData = ImmutableGaugeData.create(
    Arrays.asList(
        ImmutableDoublePointData.create(
            1000000000L, // startEpochNanos
            2000000000L, // epochNanos
            Attributes.of(AttributeKey.stringKey("host"), "server1"),
            85.5 // value
        )
    )
);

TestMetricData gaugeMetric = TestMetricData.builder()
    .setName("cpu.usage")
    .setDescription("CPU usage percentage")
    .setUnit("%")
    .setDoubleGaugeData(gaugeData)
    .setResource(Resource.builder()
        .put(ResourceAttributes.SERVICE_NAME, "monitoring-service")
        .build())
    .build();

// Create a counter sum metric
SumData<LongPointData> sumData = ImmutableSumData.create(
    true, // isMonotonic
    AggregationTemporality.CUMULATIVE,
    Arrays.asList(
        ImmutableLongPointData.create(
            1000000000L,
            2000000000L,
            Attributes.of(AttributeKey.stringKey("endpoint"), "/api/users"),
            42L // value
        )
    )
);

TestMetricData counterMetric = TestMetricData.builder()
    .setName("http.requests.total")
    .setDescription("Total number of HTTP requests")
    .setUnit("1")
    .setLongSumData(sumData)
    .build();

// Create a histogram metric
HistogramData histogramData = ImmutableHistogramData.create(
    AggregationTemporality.CUMULATIVE,
    Arrays.asList(
        ImmutableHistogramPointData.create(
            1000000000L,
            2000000000L,
            Attributes.of(AttributeKey.stringKey("method"), "GET"),
            1250.5, // sum
            false, // hasMin
            0.0, // min
            false, // hasMax
            0.0, // max
            Arrays.asList(0.0, 10.0, 50.0, 100.0, 500.0), // boundaries
            Arrays.asList(5L, 15L, 25L, 10L, 3L), // counts
            100L // total count
        )
    )
);

TestMetricData histogramMetric = TestMetricData.builder()
    .setName("http.request.duration")
    .setDescription("HTTP request duration")
    .setUnit("ms")
    .setHistogramData(histogramData)
    .build();

Log Record Data Builder

Builder for creating test LogRecordData instances with full control over log properties and metadata.

abstract class TestLogRecordData implements LogRecordData {
    // Factory method
    static Builder builder();
    
    static class Builder {
        // Basic properties
        Builder setResource(Resource resource);
        Builder setInstrumentationScopeInfo(InstrumentationScopeInfo instrumentationScopeInfo);
        
        // Timing
        Builder setTimestamp(Instant instant);
        Builder setTimestamp(long timestamp, TimeUnit unit);
        Builder setObservedTimestamp(Instant instant);
        Builder setObservedTimestamp(long timestamp, TimeUnit unit);
        
        // Content and severity
        Builder setBody(String body);
        Builder setBodyValue(Value<?> body);
        Builder setSeverity(Severity severity);
        Builder setSeverityText(String severityText);
        
        // Context and metadata
        Builder setSpanContext(SpanContext spanContext);
        Builder setAttributes(Attributes attributes);
        Builder setTotalAttributeCount(int totalAttributeCount);
        Builder setEventName(String eventName);
        
        // Build method
        TestLogRecordData build();
    }
}

Usage examples:

// Create a basic log record
TestLogRecordData logRecord = TestLogRecordData.builder()
    .setBody("User authentication successful")
    .setSeverity(Severity.INFO)
    .setSeverityText("INFO")
    .setTimestamp(Instant.now())
    .setObservedTimestamp(Instant.now().plusMillis(10))
    .setAttributes(Attributes.of(
        AttributeKey.stringKey("user.id"), "12345",
        AttributeKey.stringKey("auth.method"), "oauth2"
    ))
    .build();

// Create a log record with span context
SpanContext spanContext = SpanContext.create(
    "12345678901234567890123456789012", 
    "1234567890123456", 
    TraceFlags.getSampled(), 
    TraceState.getDefault()
);

TestLogRecordData contextualLog = TestLogRecordData.builder()
    .setBody("Processing user request")
    .setSeverity(Severity.DEBUG)
    .setSpanContext(spanContext)
    .setTimestamp(1000000000L, TimeUnit.NANOSECONDS)
    .setAttributes(Attributes.of(
        AttributeKey.stringKey("request.id"), "req-789"
    ))
    .setResource(Resource.builder()
        .put(ResourceAttributes.SERVICE_NAME, "user-service")
        .put(ResourceAttributes.SERVICE_VERSION, "1.2.3")
        .build())
    .build();

// Create an error log record
TestLogRecordData errorLog = TestLogRecordData.builder()
    .setBody("Database connection failed")
    .setSeverity(Severity.ERROR)
    .setSeverityText("ERROR")
    .setTimestamp(Instant.parse("2023-01-01T12:00:00Z"))
    .setAttributes(Attributes.of(
        AttributeKey.stringKey("db.host"), "db.example.com",
        AttributeKey.longKey("db.port"), 5432L,
        AttributeKey.stringKey("error.type"), "ConnectionTimeout"
    ))
    .setTotalAttributeCount(3)
    .build();

// Create a structured log with complex body
Value<?> structuredBody = Value.of(Map.of(
    "event", "user.created",
    "user_id", 12345,
    "metadata", Map.of(
        "source", "registration_form",
        "campaign", "spring_2023"
    )
));

TestLogRecordData structuredLog = TestLogRecordData.builder()
    .setBodyValue(structuredBody)
    .setSeverity(Severity.INFO)
    .setEventName("user.created")
    .setTimestamp(Instant.now())
    .build();

Advanced Builder Patterns

Reusable Builder Configuration

// Create a base builder with common configuration
TestSpanData.Builder baseSpanBuilder = TestSpanData.builder()
    .setResource(Resource.builder()
        .put(ResourceAttributes.SERVICE_NAME, "test-service")
        .put(ResourceAttributes.SERVICE_VERSION, "1.0.0")
        .build())
    .setInstrumentationScopeInfo(
        InstrumentationScopeInfo.create("test-instrumentation", "1.0.0"));

// Create multiple spans with shared configuration
TestSpanData span1 = baseSpanBuilder
    .setName("operation-1")
    .setStartEpochNanos(1000000000L)
    .setEndEpochNanos(1100000000L)
    .build();

TestSpanData span2 = baseSpanBuilder
    .setName("operation-2")
    .setStartEpochNanos(1200000000L)
    .setEndEpochNanos(1300000000L)
    .build();

Building Test Traces

// Create a complete trace with parent-child relationships
String traceId = "12345678901234567890123456789012";

// Parent span
TestSpanData parentSpan = TestSpanData.builder()
    .setSpanContext(SpanContext.create(traceId, "1111111111111111", 
        TraceFlags.getSampled(), TraceState.getDefault()))
    .setName("parent-operation")
    .setKind(SpanKind.SERVER)
    .setStartEpochNanos(1000000000L)
    .setEndEpochNanos(5000000000L)
    .build();

// Child spans
TestSpanData child1 = TestSpanData.builder()
    .setSpanContext(SpanContext.create(traceId, "2222222222222222", 
        TraceFlags.getSampled(), TraceState.getDefault()))
    .setParentSpanContext(parentSpan.getSpanContext())
    .setName("child-operation-1")
    .setKind(SpanKind.INTERNAL)
    .setStartEpochNanos(1500000000L)
    .setEndEpochNanos(2500000000L)
    .build();

TestSpanData child2 = TestSpanData.builder()
    .setSpanContext(SpanContext.create(traceId, "3333333333333333", 
        TraceFlags.getSampled(), TraceState.getDefault()))
    .setParentSpanContext(parentSpan.getSpanContext())
    .setName("child-operation-2")
    .setKind(SpanKind.INTERNAL)
    .setStartEpochNanos(3000000000L)
    .setEndEpochNanos(4000000000L)
    .build();

// Use in assertions
List<SpanData> traceSpans = Arrays.asList(parentSpan, child1, child2);
assertTraces(traceSpans)
    .hasTracesSatisfyingExactly(
        trace -> trace.hasSpansSatisfyingExactly(
            span -> assertThat(span).hasName("parent-operation"),
            span -> assertThat(span).hasName("child-operation-1"),
            span -> assertThat(span).hasName("child-operation-2")
        )
    );

Types

// Core OpenTelemetry data interfaces implemented by test builders
interface SpanData {
    SpanContext getSpanContext();
    SpanContext getParentSpanContext();
    Resource getResource();
    InstrumentationScopeInfo getInstrumentationScopeInfo();
    String getName();
    SpanKind getKind();
    long getStartEpochNanos();
    long getEndEpochNanos();
    Attributes getAttributes();
    List<EventData> getEvents();
    List<LinkData> getLinks();
    StatusData getStatus();
    boolean hasEnded();
    int getTotalRecordedEvents();
    int getTotalRecordedLinks();
    int getTotalAttributeCount();
}

interface MetricData {
    Resource getResource();
    InstrumentationScopeInfo getInstrumentationScopeInfo();
    String getName();
    String getDescription();
    String getUnit();
    MetricDataType getType();
    Data<?> getData();
}

interface LogRecordData {
    Resource getResource();
    InstrumentationScopeInfo getInstrumentationScopeInfo();
    long getTimestampEpochNanos();
    long getObservedTimestampEpochNanos();
    SpanContext getSpanContext();
    Severity getSeverity();
    String getSeverityText();
    Value<?> getBodyValue();
    Attributes getAttributes();
    int getTotalAttributeCount();
}

// Supporting data types
interface Attributes {
    Map<AttributeKey<?>, Object> asMap();
    int size();
    boolean isEmpty();
}

interface EventData {
    String getName();
    long getEpochNanos();
    Attributes getAttributes();
    int getTotalAttributeCount();
}

interface LinkData {
    SpanContext getSpanContext();
    Attributes getAttributes();
    int getTotalAttributeCount();
}

interface StatusData {
    StatusCode getStatusCode();
    String getDescription();
}

// Enums and value types
enum SpanKind { INTERNAL, SERVER, CLIENT, PRODUCER, CONSUMER }
enum StatusCode { UNSET, OK, ERROR }
enum Severity { TRACE, DEBUG, INFO, WARN, ERROR, FATAL }
enum TraceFlags { /* flag values */ }

class SpanContext {
    static SpanContext create(String traceId, String spanId, TraceFlags traceFlags, TraceState traceState);
    String getTraceId();
    String getSpanId();
    TraceFlags getTraceFlags();
    TraceState getTraceState();
}

Install with Tessl CLI

npx tessl i tessl/maven-io-opentelemetry--opentelemetry-sdk-testing

docs

assertions.md

exporters.md

index.md

junit-integration.md

test-builders.md

utilities.md

tile.json