Header background

Creating a JUnit extension for tracing tests with OpenTelemetry

Unit tests are at the very bottom of the testing pyramid, making them the foundation of all other tests needed to ensure bug-free software. Unsurprisingly, to avoid unpleasant surprises for our customers, we run millions of tests at Dynatrace, and many of them are JUnit tests.

When you are running so many tests, it’s natural to find some issues and bugs, and thankfully we have a very good automated system in our company that takes measures when this happens.

However, there were moments when our unit tests took a long time, and we wanted to understand why to improve them. In our setup, it would have been a lot of work to learn how the tests were parallelized, if one was taking longer than the rest, and so on. The data was there, but we needed OpenTelemetry to understand it.

So, we decided to create our own extension to simplify this process: the JUnit Jupiter OpenTelemetry Extension.

Before we explain how it works, let’s go over the basics.

What is JUnit?

As mentioned, unit tests focus on the smallest parts of the code: classes and methods. On average, thousands of unit tests run simultaneously, and frameworks like JUnit make this possible.

JUnit is an open source framework built specifically to test Java, our main developing language at Dynatrace. Here’s an example JUnit test:

@Test
void testAddition () {
int i = 1;
int j = 2;
assertEquals (expected: 3, actual: i + j);
}

What is OpenTelemetry?

OpenTelemetry is an open-source tool for gathering telemetry data, which includes logs, traces, and even metrics. This data can easily be sent to different endpoints and used further or displayed. For example, one could start a span, which, as the name suggests, spans something, at the start of a method and end it at the end of it. Attributes, such as the operating system, can be added to it. All those spans then make up a trace.

Learn more about OpenTelemetry here.

How to set up the JUnit Jupiter OpenTelemetry Extension

To get going with the extension, we need to add the dependency. In this example, we use Gradle to manage dependencies. (It’s also possible to use Maven; please see the instructions here on how to do it.)

Enter the following text to import what you need:

dependencies {
testImplementation 'com.dynatrace.oss.opentelemetry:junit.jupiter.exporter:${version}'
}

After this step, we only have to tell our tests to use the extension.

One way to do this is to add an annotation over our tests:

@ExtendWith(TracingExtension.class)
public class Test {

What’s the result?

At this point, you might be wondering what all of this looks like.

To see the tests in a UI, I set up a local Jaeger environment before running the tests. Here is what the result looks like:

A screenshot of the Junit extension test

You can also export it to different endpoints, such as Dynatrace and Zipkin, or directly to an OpenTelemetry collector for further distribution.

With that, we can see that we have a test class called MyInternalTest. The tests are executed sequentially, and the last one failed.

The dashboard displays which JUnit tests are running and how long each takes. This helps see at first glance which tests take up most of the time and quickly discover the root cause of any slowdowns.

Try it yourself!

The JUnit Jupiter Open Telemetry Extension is open source and free to use. You can try it out by downloading it from our GitHub repository.