-
Notifications
You must be signed in to change notification settings - Fork 332
Add unit and instrumentation test documentation for JUnit #11097
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: bbujon/groovy-to-java-instrumented-tests
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,350 @@ | ||||||||||||||||||||||||||
| # How to Test With JUnit Guide | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| This guide covers the JUnit 5 testing utilities for writing instrumentation and unit tests. | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| ## Config injection with `@WithConfig` | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| `@WithConfig` declares configuration overrides for tests. It injects system properties (`dd.` prefix) or environment variables (`DD_` prefix) and rebuilds the `Config` singleton before each test. | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| ### Class-level config | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| Applies to all tests in the class: | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| ```java | ||||||||||||||||||||||||||
| @WithConfig(key = "service", value = "my-service") | ||||||||||||||||||||||||||
| @WithConfig(key = "trace.analytics.enabled", value = "true") | ||||||||||||||||||||||||||
| class MyTest extends DDJavaSpecification { | ||||||||||||||||||||||||||
| @Test | ||||||||||||||||||||||||||
| void test() { | ||||||||||||||||||||||||||
| // dd.service=my-service and dd.trace.analytics.enabled=true are set | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| ### Method-level config | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| Applies to a single test method, in addition to class-level config: | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| ```java | ||||||||||||||||||||||||||
| @WithConfig(key = "service", value = "my-service") | ||||||||||||||||||||||||||
| class MyTest extends DDJavaSpecification { | ||||||||||||||||||||||||||
| @Test | ||||||||||||||||||||||||||
| @WithConfig(key = "trace.resolver.enabled", value = "false") | ||||||||||||||||||||||||||
| void testWithResolverDisabled() { | ||||||||||||||||||||||||||
| // dd.service=my-service AND dd.trace.resolver.enabled=false | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| @Test | ||||||||||||||||||||||||||
| void testWithDefaults() { | ||||||||||||||||||||||||||
| // only dd.service=my-service | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| ### Environment variables | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| Use `env = true` to set an environment variable instead of a system property: | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| ```java | ||||||||||||||||||||||||||
| @WithConfig(key = "AGENT_HOST", value = "localhost", env = true) | ||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| ### Raw keys (no auto-prefix) | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| Use `addPrefix = false` to skip the automatic `dd.`/`DD_` prefix: | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| ```java | ||||||||||||||||||||||||||
| @WithConfig(key = "OTEL_SERVICE_NAME", value = "test", env = true, addPrefix = false) | ||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| ### Config with constant references | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| Annotation values accept compile-time constants: | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| ```java | ||||||||||||||||||||||||||
| @WithConfig(key = TracerConfig.TRACE_RESOLVER_ENABLED, value = "false") | ||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| ### Inheritance | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| `@WithConfig` on a superclass applies to all subclasses. When a subclass adds its own `@WithConfig`, both the parent's and the subclass's configs are applied (parent first, subclass second). This allows base classes to set shared config while subclasses add specifics: | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| ```java | ||||||||||||||||||||||||||
| @WithConfig(key = "integration.opentelemetry.experimental.enabled", value = "true") | ||||||||||||||||||||||||||
| abstract class AbstractOtelTest extends AbstractInstrumentationTest { } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| @WithConfig(key = "trace.propagation.style", value = "b3multi") | ||||||||||||||||||||||||||
| class B3MultiTest extends AbstractOtelTest { | ||||||||||||||||||||||||||
| // Both configs are active | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| ### Composed annotations | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| Bundle multiple configs into a reusable annotation: | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| ```java | ||||||||||||||||||||||||||
| @Retention(RetentionPolicy.RUNTIME) | ||||||||||||||||||||||||||
| @Target({ElementType.TYPE, ElementType.METHOD}) | ||||||||||||||||||||||||||
| @WithConfig(key = "iast.enabled", value = "true") | ||||||||||||||||||||||||||
| @WithConfig(key = "iast.detection.mode", value = "FULL") | ||||||||||||||||||||||||||
| @WithConfig(key = "iast.redaction.enabled", value = "false") | ||||||||||||||||||||||||||
| public @interface IastFullDetection {} | ||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| Then reuse across test classes: | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| ```java | ||||||||||||||||||||||||||
| @IastFullDetection | ||||||||||||||||||||||||||
| class IastTagTest extends DDJavaSpecification { } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| @IastFullDetection | ||||||||||||||||||||||||||
| class IastReporterTest extends DDJavaSpecification { } | ||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| ### Imperative config injection | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| For dynamic values that can't be expressed in annotations, use the static methods directly: | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| ```java | ||||||||||||||||||||||||||
| @Test | ||||||||||||||||||||||||||
| void testDynamic() { | ||||||||||||||||||||||||||
| String port = startServer(); | ||||||||||||||||||||||||||
| WithConfigExtension.injectSysConfig("trace.agent.port", port); | ||||||||||||||||||||||||||
| // ... | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| ### Lifecycle | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| Config is rebuilt from a clean slate before each test: | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| 1. **`beforeAll`**: class-level `@WithConfig` applied + config rebuilt (available for `@BeforeAll` methods) | ||||||||||||||||||||||||||
| 2. **`beforeEach`**: properties restored, class + method `@WithConfig` applied, config rebuilt | ||||||||||||||||||||||||||
| 3. **`afterEach`**: env vars cleared, properties restored, config rebuilt | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| This means each test starts with a clean config, and method-level `@WithConfig` doesn't leak between tests. | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| ## Instrumentation tests with `AbstractInstrumentationTest` | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| `AbstractInstrumentationTest` is the JUnit 5 base class for instrumentation tests. It installs the agent once per test class, creates a shared tracer and writer, and provides trace assertion helpers. | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| ### Lifecycle | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| | Phase | Scope | What happens | | ||||||||||||||||||||||||||
| |---|---|---| | ||||||||||||||||||||||||||
| | `@BeforeAll initAll()` | Once per class | Creates tracer + writer, installs ByteBuddy agent | | ||||||||||||||||||||||||||
| | `@BeforeEach init()` | Per test | Flushes tracer, resets writer | | ||||||||||||||||||||||||||
| | `@AfterEach tearDown()` | Per test | Flushes tracer | | ||||||||||||||||||||||||||
| | `@AfterAll tearDownAll()` | Once per class | Closes tracer, removes agent transformer | | ||||||||||||||||||||||||||
|
Comment on lines
+134
to
+139
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: Apply |
||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| ### Available fields | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| - `tracer` — the DD `TracerAPI` instance (shared across tests in the class) | ||||||||||||||||||||||||||
| - `writer` — the `ListWriter` that captures traces written by the tracer | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| ### Configuring the tracer | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| The tracer can be configured at class level using the `testConfig` builder. Call it from a static initializer (runs before `@BeforeAll`): | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| ```java | ||||||||||||||||||||||||||
| class MyTest extends AbstractInstrumentationTest { | ||||||||||||||||||||||||||
| static { | ||||||||||||||||||||||||||
| testConfig.idGenerationStrategy("RANDOM").strictTraceWrites(false); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| Available settings: | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| | Method | Default | Description | | ||||||||||||||||||||||||||
| |---|---|---| | ||||||||||||||||||||||||||
| | `idGenerationStrategy(String)` | `"SEQUENTIAL"` | Span ID generation strategy | | ||||||||||||||||||||||||||
| | `strictTraceWrites(boolean)` | `true` | Enable strict trace write validation | | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| ### Basic test | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| ```java | ||||||||||||||||||||||||||
| class HttpInstrumentationTest extends AbstractInstrumentationTest { | ||||||||||||||||||||||||||
| @Test | ||||||||||||||||||||||||||
| void testHttpRequest() { | ||||||||||||||||||||||||||
| // exercise the instrumented code | ||||||||||||||||||||||||||
| makeHttpRequest("http://example.com/api"); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| // assert the trace structure | ||||||||||||||||||||||||||
| assertTraces( | ||||||||||||||||||||||||||
| trace( | ||||||||||||||||||||||||||
| span().root().operationName("http.request").resourceName("GET /api"))); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| ## Trace assertion API | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| The assertion API verifies trace structure using a fluent builder pattern. Import the static factories: | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| ```java | ||||||||||||||||||||||||||
| import static datadog.trace.agent.test.assertions.TraceMatcher.trace; | ||||||||||||||||||||||||||
| import static datadog.trace.agent.test.assertions.SpanMatcher.span; | ||||||||||||||||||||||||||
| import static datadog.trace.agent.test.assertions.TagsMatcher.*; | ||||||||||||||||||||||||||
| import static datadog.trace.agent.test.assertions.Matchers.*; | ||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| ### Asserting traces | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| `assertTraces` waits for traces to arrive (20s timeout), then verifies the structure: | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| ```java | ||||||||||||||||||||||||||
| // Single trace with 2 spans | ||||||||||||||||||||||||||
| assertTraces( | ||||||||||||||||||||||||||
| trace( | ||||||||||||||||||||||||||
| span().root().operationName("parent"), | ||||||||||||||||||||||||||
| span().childOfPrevious().operationName("child"))); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| // Multiple traces | ||||||||||||||||||||||||||
| assertTraces( | ||||||||||||||||||||||||||
| trace(span().root().operationName("trace-1")), | ||||||||||||||||||||||||||
| trace(span().root().operationName("trace-2"))); | ||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| ### Trace options | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| ```java | ||||||||||||||||||||||||||
| import static datadog.trace.agent.test.assertions.TraceAssertions.*; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| // Ignore extra traces beyond the expected ones | ||||||||||||||||||||||||||
| assertTraces(IGNORE_ADDITIONAL_TRACES, | ||||||||||||||||||||||||||
| trace(span().root().operationName("expected"))); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| // Sort traces by start time before assertion | ||||||||||||||||||||||||||
| assertTraces(SORT_BY_START_TIME, | ||||||||||||||||||||||||||
| trace(span().root().operationName("first")), | ||||||||||||||||||||||||||
| trace(span().root().operationName("second"))); | ||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| ### Span matching | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| ```java | ||||||||||||||||||||||||||
| span() | ||||||||||||||||||||||||||
| // Identity | ||||||||||||||||||||||||||
| .root() // root span (parent ID = 0) | ||||||||||||||||||||||||||
| .childOfPrevious() // child of previous span in trace | ||||||||||||||||||||||||||
| .childOf(parentSpanId) // child of specific span | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| // Properties | ||||||||||||||||||||||||||
| .operationName("http.request") // exact match | ||||||||||||||||||||||||||
| .operationName(Pattern.compile("http.*"))// regex match | ||||||||||||||||||||||||||
| .resourceName("GET /api") // exact match | ||||||||||||||||||||||||||
| .serviceName("my-service") // exact match | ||||||||||||||||||||||||||
| .type("web") // span type | ||||||||||||||||||||||||||
|
Comment on lines
+234
to
+239
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit, apply some spaces:
Suggested change
|
||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| // Error | ||||||||||||||||||||||||||
| .error() // expects error = true | ||||||||||||||||||||||||||
| .error(false) // expects error = false | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| // Duration | ||||||||||||||||||||||||||
| .durationShorterThan(Duration.ofMillis(100)) | ||||||||||||||||||||||||||
| .durationLongerThan(Duration.ofMillis(1)) | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| // Tags | ||||||||||||||||||||||||||
| .tags( | ||||||||||||||||||||||||||
| defaultTags(), // all standard DD tags | ||||||||||||||||||||||||||
| tag("http.method", is("GET")), // exact tag value | ||||||||||||||||||||||||||
| tag("db.type", is("postgres"))) | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| // Span links | ||||||||||||||||||||||||||
| .links( | ||||||||||||||||||||||||||
| SpanLinkMatcher.to(otherSpan), | ||||||||||||||||||||||||||
| SpanLinkMatcher.any()) | ||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| ### Tag matching | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| ```java | ||||||||||||||||||||||||||
| // Default DD tags (thread name, runtime ID, sampling, etc.) | ||||||||||||||||||||||||||
| defaultTags() | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| // Exact value | ||||||||||||||||||||||||||
| tag("http.status", is(200)) | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| // Custom validation | ||||||||||||||||||||||||||
| tag("response.body", validates(v -> ((String) v).contains("success"))) | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| // Any value (just check presence) | ||||||||||||||||||||||||||
| tag("custom.tag", any()) | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| // Error tags from exception | ||||||||||||||||||||||||||
| error(IOException.class) | ||||||||||||||||||||||||||
| error(IOException.class, "Connection refused") | ||||||||||||||||||||||||||
| error(new IOException("Connection refused")) | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| // Check tag presence without value check | ||||||||||||||||||||||||||
| includes("tag1", "tag2") | ||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| ### Value matchers | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| ```java | ||||||||||||||||||||||||||
| is("expected") // equality | ||||||||||||||||||||||||||
| isNull() // null check | ||||||||||||||||||||||||||
| isNonNull() // non-null check | ||||||||||||||||||||||||||
| isTrue() // boolean true | ||||||||||||||||||||||||||
| isFalse() // boolean false | ||||||||||||||||||||||||||
| matches("regex.*") // regex match | ||||||||||||||||||||||||||
| matches(Pattern.compile("...")) | ||||||||||||||||||||||||||
| validates(v -> ...) // custom predicate | ||||||||||||||||||||||||||
| any() // accept anything | ||||||||||||||||||||||||||
|
Comment on lines
+295
to
+296
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: some formatting:
Suggested change
|
||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||
|
Comment on lines
+261
to
+297
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion: Maybe format that as a list |
||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| ### Span link matching | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| ```java | ||||||||||||||||||||||||||
| // Link to a specific span | ||||||||||||||||||||||||||
| SpanLinkMatcher.to(parentSpan) | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| // Link with trace/span IDs | ||||||||||||||||||||||||||
| SpanLinkMatcher.to(traceId, spanId) | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| // Link with additional properties | ||||||||||||||||||||||||||
| SpanLinkMatcher.to(span) | ||||||||||||||||||||||||||
| .traceFlags((byte) 0x01) | ||||||||||||||||||||||||||
| .traceState("vendor=value") | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| // Accept any link | ||||||||||||||||||||||||||
| SpanLinkMatcher.any() | ||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| ### Sorting spans within a trace | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| ```java | ||||||||||||||||||||||||||
| import static datadog.trace.agent.test.assertions.TraceMatcher.SORT_BY_START_TIME; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| assertTraces( | ||||||||||||||||||||||||||
| trace( | ||||||||||||||||||||||||||
| SORT_BY_START_TIME, | ||||||||||||||||||||||||||
| span().root().operationName("parent"), | ||||||||||||||||||||||||||
| span().childOfPrevious().operationName("child"))); | ||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| ### Waiting for traces | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| ```java | ||||||||||||||||||||||||||
| // Wait until a condition is met (20s timeout) | ||||||||||||||||||||||||||
| blockUntilTracesMatch(traces -> traces.size() >= 2); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| // Wait for child spans to finish | ||||||||||||||||||||||||||
| blockUntilChildSpansFinished(3); | ||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| ### Accessing traces directly | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| For assertions not covered by the fluent API, access the writer directly: | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| ```java | ||||||||||||||||||||||||||
| writer.waitForTraces(1); | ||||||||||||||||||||||||||
| List<DDSpan> trace = writer.firstTrace(); | ||||||||||||||||||||||||||
| DDSpan span = trace.get(0); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| assertEquals("expected-op", span.getOperationName().toString()); | ||||||||||||||||||||||||||
| assertEquals(42L, span.getTag("custom.metric")); | ||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
praise: This dc is great, I would however add two items that we find in spock tests: mocks and parameterized tests.
About the order I suggest
I wonder do we want to follow the "BDD" style, to match what spock did, e.g. with comments