package metricdatatest

import "go.opentelemetry.io/otel/sdk/metric/metricdata/metricdatatest"

Package metricdatatest provides testing functionality for use with the metricdata package.

Index

Examples

Functions

func AssertAggregationsEqual

func AssertAggregationsEqual(t TestingT, expected, actual metricdata.Aggregation, opts ...Option) bool

AssertAggregationsEqual asserts that two Aggregations are equal.

Example

Code:play 

package main

import (
	"context"
	"fmt"

	sdkmetric "go.opentelemetry.io/otel/sdk/metric"
	"go.opentelemetry.io/otel/sdk/metric/metricdata"
	"go.opentelemetry.io/otel/sdk/metric/metricdata/metricdatatest"
)

func main() {
	ctx := context.Background()

	// Create a meterprovider with a reader
	reader := sdkmetric.NewManualReader()
	mp := sdkmetric.NewMeterProvider(sdkmetric.WithReader(reader))
	defer func() {
		_ = mp.Shutdown(ctx)
	}()

	// Create an instrument(eg: counter/histogram/gauge) and simulate an operation
	meter := mp.Meter("payment-service")
	counter, _ := meter.Int64Counter("payment.count")
	counter.Add(ctx, 5)

	// Collect the metrics
	rm := &metricdata.ResourceMetrics{}
	_ = reader.Collect(ctx, rm)
	got, _ := getMetrics("payment.count", rm)

	want := metricdata.Metrics{
		Data: metricdata.Sum[int64]{
			DataPoints:  []metricdata.DataPoint[int64]{{Value: 5}},
			Temporality: metricdata.CumulativeTemporality,
			IsMonotonic: true,
		},
	}

	// Verify the expected data with the received one.
	t := &mockTestingT{}

	// Compare Aggregations
	hasEqualAggregations := metricdatatest.AssertAggregationsEqual(
		t,
		want.Data,
		got.Data,
		metricdatatest.IgnoreTimestamp(),
	)
	fmt.Printf("Aggregations are equal: %t\n", hasEqualAggregations)

}

// Helper function to retrieve the metrics.
// nolint:unparam // 'bool' return value currently unused, but retained for completeness
func getMetrics(name string, rm *metricdata.ResourceMetrics) (metricdata.Metrics, bool) {
	for _, scopeMetrics := range rm.ScopeMetrics {
		for _, m := range scopeMetrics.Metrics {
			if m.Name == name {
				return m, true
			}
		}
	}
	return metricdata.Metrics{}, false
}

// mockTestingT implements the [metricdatatest.TestingT] interface for examples.
// Usually, we use [*testing.T] as a substitute.
type mockTestingT struct{}

func (*mockTestingT) Helper() {}

func (*mockTestingT) Error(...any) {}

Output:

Aggregations are equal: true

func AssertEqual

func AssertEqual[T Datatypes](t TestingT, expected, actual T, opts ...Option) bool

AssertEqual asserts that the two concrete data-types from the metricdata package are equal.

Example

Code:play 

package main

import (
	"context"
	"fmt"

	sdkmetric "go.opentelemetry.io/otel/sdk/metric"
	"go.opentelemetry.io/otel/sdk/metric/metricdata"
	"go.opentelemetry.io/otel/sdk/metric/metricdata/metricdatatest"
)

func main() {
	ctx := context.Background()

	// Create a meterprovider with a reader
	reader := sdkmetric.NewManualReader()
	mp := sdkmetric.NewMeterProvider(sdkmetric.WithReader(reader))
	defer func() {
		_ = mp.Shutdown(ctx)
	}()

	// Create an instrument(eg: counter/histogram/gauge) and simulate an operation
	meter := mp.Meter("payment-service")
	counter, _ := meter.Int64Counter("payment.requests")
	counter.Add(ctx, 5)

	// Collect the metrics
	rm := &metricdata.ResourceMetrics{}
	_ = reader.Collect(ctx, rm)
	got, _ := getMetrics("payment.requests", rm)

	want := metricdata.Metrics{
		Name: "payment.requests",
		Data: metricdata.Sum[int64]{
			DataPoints:  []metricdata.DataPoint[int64]{{Value: 5}},
			Temporality: metricdata.CumulativeTemporality,
			IsMonotonic: true,
		},
	}

	// Compare expected metrics with the received one
	t := &mockTestingT{}
	assertEqual := metricdatatest.AssertEqual(
		t,
		want,
		got,
		metricdatatest.IgnoreTimestamp(), // ignoring timestamps
	)
	fmt.Printf("Metrics are equal: %t\n", assertEqual)

}

// Helper function to retrieve the metrics.
// nolint:unparam // 'bool' return value currently unused, but retained for completeness
func getMetrics(name string, rm *metricdata.ResourceMetrics) (metricdata.Metrics, bool) {
	for _, scopeMetrics := range rm.ScopeMetrics {
		for _, m := range scopeMetrics.Metrics {
			if m.Name == name {
				return m, true
			}
		}
	}
	return metricdata.Metrics{}, false
}

// mockTestingT implements the [metricdatatest.TestingT] interface for examples.
// Usually, we use [*testing.T] as a substitute.
type mockTestingT struct{}

func (*mockTestingT) Helper() {}

func (*mockTestingT) Error(...any) {}

Output:

Metrics are equal: true

func AssertHasAttributes

func AssertHasAttributes[T Datatypes](t TestingT, actual T, attrs ...attribute.KeyValue) bool

AssertHasAttributes asserts that all Datapoints or HistogramDataPoints have all passed attrs.

Example

Code:play 

package main

import (
	"context"
	"fmt"

	"go.opentelemetry.io/otel/attribute"
	"go.opentelemetry.io/otel/metric"

	sdkmetric "go.opentelemetry.io/otel/sdk/metric"
	"go.opentelemetry.io/otel/sdk/metric/metricdata"
	"go.opentelemetry.io/otel/sdk/metric/metricdata/metricdatatest"
)

func main() {
	ctx := context.Background()

	// Create a meterprovider with a reader
	reader := sdkmetric.NewManualReader()
	mp := sdkmetric.NewMeterProvider(sdkmetric.WithReader(reader))
	defer func() {
		_ = mp.Shutdown(ctx)
	}()

	// Simulate an operation using an instrument(eg: counter)
	meter := mp.Meter("payment-service")
	counter, _ := meter.Int64Counter("payment.requests")

	// Add attribute to the measurement
	attributes := attribute.NewSet(attribute.String("payment.method", "credit_card"))
	counter.Add(ctx, 5, metric.WithAttributeSet(attributes))

	// Collect the metrics
	rm := &metricdata.ResourceMetrics{}
	_ = reader.Collect(ctx, rm)
	metrics, _ := getMetrics("payment.requests", rm)

	// Verify the attributes in the received metrics
	t := &mockTestingT{}
	hasAttributes := metricdatatest.AssertHasAttributes(t, metrics, attributes.ToSlice()...)
	fmt.Printf("Metrics contains attributes: %t\n", hasAttributes)

}

// Helper function to retrieve the metrics.
// nolint:unparam // 'bool' return value currently unused, but retained for completeness
func getMetrics(name string, rm *metricdata.ResourceMetrics) (metricdata.Metrics, bool) {
	for _, scopeMetrics := range rm.ScopeMetrics {
		for _, m := range scopeMetrics.Metrics {
			if m.Name == name {
				return m, true
			}
		}
	}
	return metricdata.Metrics{}, false
}

// mockTestingT implements the [metricdatatest.TestingT] interface for examples.
// Usually, we use [*testing.T] as a substitute.
type mockTestingT struct{}

func (*mockTestingT) Helper() {}

func (*mockTestingT) Error(...any) {}

Output:

Metrics contains attributes: true

Types

type Datatypes

Datatypes are the concrete data-types the metricdata package provides.

type Option

type Option interface {
	// contains filtered or unexported methods
}

Option allows for fine grain control over how AssertEqual operates.

func IgnoreExemplars

func IgnoreExemplars() Option

IgnoreExemplars disables checking if Exemplars are different.

Example

Code:play 

package main

import (
	"fmt"
	"time"

	"go.opentelemetry.io/otel/attribute"
	"go.opentelemetry.io/otel/sdk/metric/metricdata"
	"go.opentelemetry.io/otel/sdk/metric/metricdata/metricdatatest"
)

func main() {
	// Histogram data with Exemplars
	want := metricdata.Metrics{
		Name: "payment.duration",
		Data: metricdata.Histogram[float64]{
			DataPoints: []metricdata.HistogramDataPoint[float64]{
				{
					Count:        2,
					Sum:          224.0,
					Bounds:       []float64{0, 5, 10, 25, 50, 75, 100, 250, 500, 750, 1000},
					BucketCounts: []uint64{0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0},
					Exemplars: []metricdata.Exemplar[float64]{
						{
							FilteredAttributes: []attribute.KeyValue{
								attribute.String("payment.type", "recurring"),
							},
							Time:    time.Now(),
							Value:   15.0,
							SpanID:  []byte{1, 2, 3, 4, 5, 6, 7, 8},
							TraceID: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16},
						},
					},
				},
			},
			Temporality: metricdata.CumulativeTemporality,
		},
	}

	// Histogram data without Exemplars
	got := metricdata.Metrics{
		Name: "payment.duration",
		Data: metricdata.Histogram[float64]{
			// Aggregate measurements are different in received metrics(exemplars)
			DataPoints: []metricdata.HistogramDataPoint[float64]{
				{
					Count:        2,
					Sum:          224.0,
					Bounds:       []float64{0, 5, 10, 25, 50, 75, 100, 250, 500, 750, 1000},
					BucketCounts: []uint64{0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0},
				},
			},
			Temporality: metricdata.CumulativeTemporality,
		},
	}

	t := &mockTestingT{}

	// Compare metrics
	ignoreExemplars := metricdatatest.AssertEqual(
		t,
		want,
		got,
		metricdatatest.IgnoreExemplars(),
	)
	fmt.Printf("Metrics are equal(ignoring exemplars): %t\n", ignoreExemplars)

}

// mockTestingT implements the [metricdatatest.TestingT] interface for examples.
// Usually, we use [*testing.T] as a substitute.
type mockTestingT struct{}

func (*mockTestingT) Helper() {}

func (*mockTestingT) Error(...any) {}

Output:

Metrics are equal(ignoring exemplars): true

func IgnoreTimestamp

func IgnoreTimestamp() Option

IgnoreTimestamp disables checking if timestamps are different.

func IgnoreValue

func IgnoreValue() Option

IgnoreValue disables checking if values are different. This can be useful for non-deterministic values, like measured durations.

This will ignore the value and trace information for Exemplars; the buckets, zero count, scale, sum, max, min, and counts of ExponentialHistogramDataPoints; the buckets, sum, count, max, and min of HistogramDataPoints; the value of DataPoints.

Example

Code:play 

package main

import (
	"fmt"

	"go.opentelemetry.io/otel/sdk/metric/metricdata"
	"go.opentelemetry.io/otel/sdk/metric/metricdata/metricdatatest"
)

func main() {
	want := metricdata.Metrics{
		Name: "payment.duration",
		Data: metricdata.Histogram[float64]{
			DataPoints: []metricdata.HistogramDataPoint[float64]{
				{
					Count:        2,
					Sum:          224.0,
					Bounds:       []float64{0, 5, 10, 25, 50, 75, 100, 250, 500, 750, 1000},
					BucketCounts: []uint64{0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0},
				},
			},
			Temporality: metricdata.CumulativeTemporality,
		},
	}

	got := metricdata.Metrics{
		Name: "payment.duration",
		Data: metricdata.Histogram[float64]{
			// Aggregate measurements are different in received metrics
			DataPoints: []metricdata.HistogramDataPoint[float64]{
				{
					Count:        10,
					Sum:          0,
					Bounds:       []float64{0, 5, 10, 25, 50, 75, 100, 250, 500, 750, 1000},
					BucketCounts: []uint64{1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0},
				},
			},
			Temporality: metricdata.CumulativeTemporality,
		},
	}

	t := &mockTestingT{}

	// Compare metrics without values
	ignoreValue := metricdatatest.AssertEqual(
		t,
		want,
		got,
		metricdatatest.IgnoreValue(),
	)
	fmt.Printf("Metrics are equal(ignoring values): %t\n", ignoreValue)

}

// mockTestingT implements the [metricdatatest.TestingT] interface for examples.
// Usually, we use [*testing.T] as a substitute.
type mockTestingT struct{}

func (*mockTestingT) Helper() {}

func (*mockTestingT) Error(...any) {}

Output:

Metrics are equal(ignoring values): true

type TestingT

type TestingT interface {
	Helper()

	Error(...any)
}

TestingT is an interface that implements testing.T, but without the private method of testing.TB, so other testing packages can rely on it as well. The methods in this interface must match the testing.TB interface.

Source Files

assertion.go comparisons.go

Version
v1.43.0 (latest)
Published
Apr 3, 2026
Platform
linux/amd64
Imports
7 packages
Last checked
1 day ago

Tools for package owners.