OpenTelemetry SDK Testing utilities providing comprehensive testing support for OpenTelemetry Java SDK applications with AssertJ assertions, in-memory exporters, and JUnit integration.
—
JUnit 4 and JUnit 5 extensions that automatically set up OpenTelemetry SDK for testing with in-memory exporters and convenient access to collected telemetry data. These extensions handle the lifecycle of OpenTelemetry configuration and provide easy access to captured spans, metrics, and logs.
JUnit 5 extension that configures OpenTelemetry SDK with in-memory exporters and provides convenient access to collected telemetry data.
class OpenTelemetryExtension implements BeforeEachCallback, BeforeAllCallback, AfterAllCallback {
// Factory method
static OpenTelemetryExtension create();
// OpenTelemetry access
OpenTelemetry getOpenTelemetry();
// Collected data access
List<SpanData> getSpans();
List<MetricData> getMetrics();
List<LogRecordData> getLogRecords();
// Assertion helper
TracesAssert assertTraces();
// Data management
void clearSpans();
void clearMetrics();
void clearLogRecords();
}Usage examples:
import io.opentelemetry.sdk.testing.junit5.OpenTelemetryExtension;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.*;
class MyServiceTest {
@RegisterExtension
static final OpenTelemetryExtension otelTesting = OpenTelemetryExtension.create();
@Test
void testServiceOperation() {
// Get the configured OpenTelemetry instance
OpenTelemetry openTelemetry = otelTesting.getOpenTelemetry();
// Initialize your service with the test OpenTelemetry
MyService service = new MyService(openTelemetry);
// Execute the operation that should create telemetry
service.performOperation("test-input");
// Validate collected spans
assertThat(otelTesting.getSpans())
.hasSize(2)
.satisfiesExactly(
span -> assertThat(span)
.hasName("service.operation")
.hasAttribute(AttributeKey.stringKey("input"), "test-input"),
span -> assertThat(span)
.hasName("database.query")
.hasKind(SpanKind.CLIENT)
);
// Validate collected metrics
assertThat(otelTesting.getMetrics())
.hasSize(1)
.first()
.hasName("operation.duration")
.hasDoubleGaugeSatisfying(gauge ->
gauge.hasPointsSatisfying(point ->
point.hasValue(greaterThan(0.0))
)
);
// Validate collected logs
assertThat(otelTesting.getLogRecords())
.hasSize(1)
.first()
.hasBody("Operation completed successfully")
.hasSeverity(Severity.INFO);
}
@Test
void testWithTraceAssertions() {
OpenTelemetry openTelemetry = otelTesting.getOpenTelemetry();
MyService service = new MyService(openTelemetry);
service.performNestedOperations();
// Use the built-in trace assertions
otelTesting.assertTraces()
.hasTracesSatisfyingExactly(
trace -> trace.hasSpansSatisfyingExactly(
span -> assertThat(span).hasName("parent.operation"),
span -> assertThat(span).hasName("child.operation.1"),
span -> assertThat(span).hasName("child.operation.2")
)
);
}
@Test
void testDataClearing() {
OpenTelemetry openTelemetry = otelTesting.getOpenTelemetry();
MyService service = new MyService(openTelemetry);
// First operation
service.performOperation("first");
assertThat(otelTesting.getSpans()).hasSize(1);
// Clear data and perform second operation
otelTesting.clearSpans();
service.performOperation("second");
assertThat(otelTesting.getSpans()).hasSize(1);
assertThat(otelTesting.getSpans().get(0))
.hasAttribute(AttributeKey.stringKey("input"), "second");
}
}JUnit 4 rule that provides the same functionality as the JUnit 5 extension for projects using JUnit 4.
class OpenTelemetryRule extends ExternalResource {
// Factory method
static OpenTelemetryRule create();
// OpenTelemetry access (identical to JUnit 5 extension)
OpenTelemetry getOpenTelemetry();
// Collected data access
List<SpanData> getSpans();
List<MetricData> getMetrics();
List<LogRecordData> getLogRecords();
// Data management
void clearSpans();
void clearMetrics();
void clearLogRecords();
}Usage examples:
import io.opentelemetry.sdk.testing.junit4.OpenTelemetryRule;
import org.junit.Rule;
import org.junit.Test;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.*;
public class MyServiceTest {
@Rule
public final OpenTelemetryRule otelTesting = OpenTelemetryRule.create();
@Test
public void testServiceOperation() {
// Get the configured OpenTelemetry instance
OpenTelemetry openTelemetry = otelTesting.getOpenTelemetry();
// Initialize your service with the test OpenTelemetry
MyService service = new MyService(openTelemetry);
// Execute the operation
service.performOperation("test-input");
// Validate collected data
assertThat(otelTesting.getSpans())
.hasSize(1)
.first()
.hasName("service.operation")
.hasAttribute(AttributeKey.stringKey("input"), "test-input");
}
@Test
public void testMultipleOperations() {
OpenTelemetry openTelemetry = otelTesting.getOpenTelemetry();
MyService service = new MyService(openTelemetry);
// Perform multiple operations
service.performOperation("first");
service.performOperation("second");
// Validate all collected spans
assertThat(otelTesting.getSpans())
.hasSize(2)
.extracting(SpanData::getName)
.containsExactly("service.operation", "service.operation");
// Validate attributes of specific spans
assertThat(otelTesting.getSpans().get(0))
.hasAttribute(AttributeKey.stringKey("input"), "first");
assertThat(otelTesting.getSpans().get(1))
.hasAttribute(AttributeKey.stringKey("input"), "second");
}
}Both the JUnit 5 extension and JUnit 4 rule automatically configure:
The extensions handle the complete lifecycle:
// Data collection happens automatically during test execution
OpenTelemetry openTelemetry = otelTesting.getOpenTelemetry();
// Create telemetry in your code
Tracer tracer = openTelemetry.getTracer("test");
Span span = tracer.spanBuilder("operation").startSpan();
span.end();
// Data is immediately available for assertions
List<SpanData> spans = otelTesting.getSpans(); // Contains the created span@Test
void testWithCustomConfiguration() {
OpenTelemetry openTelemetry = otelTesting.getOpenTelemetry();
// Configure your service with additional resources or instrumentation
MyService service = MyService.builder()
.withOpenTelemetry(openTelemetry)
.withResource(Resource.getDefault().merge(
Resource.builder()
.put(ResourceAttributes.SERVICE_NAME, "test-service")
.put(ResourceAttributes.SERVICE_VERSION, "1.0.0")
.build()))
.build();
service.performOperation();
// Validate resource attributes
assertThat(otelTesting.getSpans())
.first()
.hasResourceSatisfying(resource ->
assertThat(resource)
.hasAttribute(ResourceAttributes.SERVICE_NAME, "test-service")
.hasAttribute(ResourceAttributes.SERVICE_VERSION, "1.0.0")
);
}@Test
void testErrorHandling() {
OpenTelemetry openTelemetry = otelTesting.getOpenTelemetry();
MyService service = new MyService(openTelemetry);
// Trigger an error condition
assertThrows(ServiceException.class, () ->
service.performOperation("invalid-input"));
// Validate error span
assertThat(otelTesting.getSpans())
.first()
.hasName("service.operation")
.hasStatusSatisfying(status ->
status.hasCode(StatusCode.ERROR)
.hasDescription("Invalid input provided")
)
.hasException(ServiceException.class);
// Validate error logs
assertThat(otelTesting.getLogRecords())
.first()
.hasSeverity(Severity.ERROR)
.hasBody(containsString("Failed to process input"));
}@ParameterizedTest
@ValueSource(strings = {"input1", "input2", "input3"})
void testParameterizedOperations(String input) {
OpenTelemetry openTelemetry = otelTesting.getOpenTelemetry();
MyService service = new MyService(openTelemetry);
service.performOperation(input);
assertThat(otelTesting.getSpans())
.hasSize(1)
.first()
.hasAttribute(AttributeKey.stringKey("input"), input);
// Clear data for next parameterized test
otelTesting.clearSpans();
}// JUnit 5 callback interfaces
interface BeforeEachCallback {
void beforeEach(ExtensionContext context) throws Exception;
}
interface BeforeAllCallback {
void beforeAll(ExtensionContext context) throws Exception;
}
interface AfterAllCallback {
void afterAll(ExtensionContext context) throws Exception;
}
// JUnit 4 rule base class
abstract class ExternalResource implements TestRule {
protected void before() throws Throwable;
protected void after();
}
// OpenTelemetry SDK components configured by extensions
interface OpenTelemetry {
TracerProvider getTracerProvider();
MeterProvider getMeterProvider();
LoggerProvider getLoggerProvider();
}
// Data types returned by extensions
interface SpanData { /* Completed span data */ }
interface MetricData { /* Metric measurement data */ }
interface LogRecordData { /* Log record data */ }
// Assertion helper type
class TracesAssert extends AbstractAssert<TracesAssert, Collection<List<SpanData>>> {
TracesAssert hasTracesSatisfyingExactly(Consumer<TraceAssert>... assertions);
}Install with Tessl CLI
npx tessl i tessl/maven-io-opentelemetry--opentelemetry-sdk-testing