This is a read-only subtree split of https://github.com/open-telemetry/opentelemetry-php-contrib.
This package provides testing utilities for OpenTelemetry PHP instrumentations. It includes tools to help test and validate trace structures, span relationships, and other OpenTelemetry-specific functionality.
The TraceStructureAssertionTrait
provides methods to assess if spans match an expected trace structure. It's particularly useful for testing complex trace hierarchies and relationships between spans.
Key features:
- Support for hierarchical span relationships
- Verification of span names, kinds, attributes, events, and status
- Flexible matching with strict and non-strict modes
- Support for PHPUnit matchers/constraints for more flexible assertions
- Detailed error messages for failed assertions
- Two interfaces: array-based and fluent
- PHP 7.4 or higher
- OpenTelemetry SDK and API (for testing)
- PHPUnit 9.5 or higher
Add the trait to your test class:
use OpenTelemetry\TestUtils\TraceStructureAssertionTrait;
use PHPUnit\Framework\TestCase;
class MyTest extends TestCase
{
use TraceStructureAssertionTrait;
// Your test methods...
}
Use the assertTraceStructure
method to verify trace structures using an array-based approach:
public function testTraceStructure(): void
{
// Create spans using the OpenTelemetry SDK
// ...
// Define the expected structure
$expectedStructure = [
[
'name' => 'root-span',
'kind' => SpanKind::KIND_SERVER,
'children' => [
[
'name' => 'child-span',
'kind' => SpanKind::KIND_INTERNAL,
'attributes' => [
'attribute.one' => 'value1',
'attribute.two' => 42,
],
'events' => [
[
'name' => 'event.processed',
'attributes' => [
'processed.id' => 'abc123',
],
],
],
],
[
'name' => 'another-child-span',
'kind' => SpanKind::KIND_CLIENT,
'status' => [
'code' => StatusCode::STATUS_ERROR,
'description' => 'Something went wrong',
],
],
],
],
];
// Assert the trace structure
$this->assertTraceStructure($spans, $expectedStructure);
}
The assertTraceStructure
method takes the following parameters:
$spans
: An array or ArrayObject of spans (typically from an InMemoryExporter)$expectedStructure
: An array defining the expected structure of the trace$strict
(optional): Whether to perform strict matching (all attributes must match)
Use the assertTrace
method to verify trace structures using a fluent, chainable interface:
public function testTraceStructure(): void
{
// Create spans using the OpenTelemetry SDK
// ...
// Assert the trace structure using the fluent interface
$this->assertTrace($spans)
->hasRootSpan('root-span')
->withKind(SpanKind::KIND_SERVER)
->hasChild('child-span')
->withKind(SpanKind::KIND_INTERNAL)
->withAttribute('attribute.one', 'value1')
->withAttribute('attribute.two', 42)
->hasEvent('event.processed')
->withAttribute('processed.id', 'abc123')
->end()
->end()
->hasChild('another-child-span')
->withKind(SpanKind::KIND_CLIENT)
->withStatus(StatusCode::STATUS_ERROR, 'Something went wrong')
->end()
->end();
}
The fluent interface provides the following methods:
TraceAssertion:
hasRootSpan(string|Constraint $name)
: Assert that the trace has a root span with the given namehasRootSpans(int $count)
: Assert that the trace has the expected number of root spansinStrictMode()
: Enable strict mode for all assertions
SpanAssertion:
withKind(int|Constraint $kind)
: Assert that the span has the expected kindwithAttribute(string $key, mixed|Constraint $value)
: Assert that the span has an attribute with the expected key and valuewithAttributes(array $attributes)
: Assert that the span has the expected attributeswithStatus(int|Constraint $code, string|Constraint|null $description = null)
: Assert that the span has the expected statushasEvent(string|Constraint $name)
: Assert that the span has an event with the expected namehasChild(string|Constraint $name)
: Assert that the span has a child span with the expected namehasChildren(int $count)
: Assert that the span has the expected number of childrenend()
: Return to the parent assertion
SpanEventAssertion:
withAttribute(string $key, mixed|Constraint $value)
: Assert that the event has an attribute with the expected key and valuewithAttributes(array $attributes)
: Assert that the event has the expected attributesend()
: Return to the parent span assertion
You can use PHPUnit constraints/matchers for more flexible assertions with both interfaces:
use PHPUnit\Framework\Constraint\Callback;
use PHPUnit\Framework\Constraint\IsIdentical;
use PHPUnit\Framework\Constraint\IsType;
use PHPUnit\Framework\Constraint\RegularExpression;
use PHPUnit\Framework\Constraint\StringContains;
// Define the expected structure with matchers
$expectedStructure = [
[
'name' => 'root-span',
'kind' => new IsIdentical(SpanKind::KIND_SERVER),
'attributes' => [
'string.attribute' => new StringContains('World'),
'numeric.attribute' => new Callback(function ($value) {
return $value > 40 || $value === 42;
}),
'boolean.attribute' => new IsType('boolean'),
'array.attribute' => new Callback(function ($value) {
return is_array($value) && count($value) === 3 && in_array('b', $value);
}),
],
'children' => [
[
'name' => new RegularExpression('/child-span-\d+/'),
'kind' => SpanKind::KIND_INTERNAL,
'attributes' => [
'timestamp' => new IsType('integer'),
],
'events' => [
[
'name' => 'process.start',
'attributes' => [
'process.id' => new IsType('integer'),
'process.name' => new StringContains('process'),
],
],
],
],
],
],
];
// Assert the trace structure with matchers
$this->assertTraceStructure($spans, $expectedStructure);
use PHPUnit\Framework\Constraint\Callback;
use PHPUnit\Framework\Constraint\IsIdentical;
use PHPUnit\Framework\Constraint\IsType;
use PHPUnit\Framework\Constraint\RegularExpression;
use PHPUnit\Framework\Constraint\StringContains;
// Assert the trace structure using the fluent interface with matchers
$this->assertTrace($spans)
->hasRootSpan('root-span')
->withKind(new IsIdentical(SpanKind::KIND_SERVER))
->withAttribute('string.attribute', new StringContains('World'))
->withAttribute('numeric.attribute', new Callback(function ($value) {
return $value > 40 || $value === 42;
}))
->withAttribute('boolean.attribute', new IsType('boolean'))
->withAttribute('array.attribute', new Callback(function ($value) {
return is_array($value) && count($value) === 3 && in_array('b', $value);
}))
->hasChild(new RegularExpression('/child-span-\d+/'))
->withKind(SpanKind::KIND_INTERNAL)
->withAttribute('timestamp', new IsType('integer'))
->hasEvent('process.start')
->withAttribute('process.id', new IsType('integer'))
->withAttribute('process.name', new StringContains('process'))
->end()
->end()
->end();
Supported PHPUnit matchers include:
StringContains
for partial string matchingRegularExpression
for pattern matchingIsIdentical
for strict equalityIsEqual
for loose equalityIsType
for type checkingCallback
for custom validation logic
$ composer require --dev open-telemetry/test-utils
From the Test Utils subdirectory:
$ composer install
$ ./vendor/bin/phpunit tests