Hey there, Java developers! Ever found yourself scrolling through pages of Surefire output, trying to piece together which JUnit 5 tests passed or failed, and in what order they ran? It can feel like searching for a needle in a haystack, right?

Well, the Maven Surefire JUnit 5 Tree Reporter project aims to make that experience significantly better. This nifty little reporter plugin steps in to provide a more structured and readable output of your JUnit 5 test executions directly in your Maven build logs.

https://github.com/fabriciorby/maven-surefire-junit5-tree-reporter?tab=readme-ov-file

Instead of a flat list of test results, this reporter presents your tests in a hierarchical tree view, mirroring your test class structure. This makes it instantly easier to:

  • Visualize your test organization: See at a glance how your tests are grouped within classes and nested classes.
  • Pinpoint failures quickly: Failed tests are clearly highlighted within their context, making it much faster to identify the source of the problem.
  • Understand test execution flow: The tree structure can give you a better sense of the order in which your tests and their lifecycle methods are being executed.

Let's take a whirlwird look at how we can improve the out-of-the-box unit test reporting in a couple of easy steps.

Here's a simple junit 5 test with Nested and Parameterized tests.

class TreeReporterTest {

	@Test
	void simpleTest() {}

	@Nested
	class NestedTests {

		@Test
		void simpleNestedTest() {}

		@ParameterizedTest
		@ValueSource(strings = {"a", "b"})
		void simpleNestedParameterisedTest(String str) {}

	}

}

What does this output look like?

> mvn test
...
[INFO] -------------------------------------------------------
[INFO]  T E S T S
[INFO] -------------------------------------------------------
[INFO] Running cf.dev.presentation.TreeReporterTest
[INFO] Running cf.dev.presentation.TreeReporterTest$NestedTests
[INFO] Tests run: 4, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.024 s -- in cf.dev.presentation.TreeReporterTest$NestedTests
[INFO] Tests run: 0, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.043 s -- in cf.dev.presentation.TreeReporterTest
[INFO] 
[INFO] Results:
[INFO] 
[WARNING] Tests run: 4, Failures: 0, Errors: 0, Skipped: 0
[INFO] 

This can be described as functional output at best. So how do we get that fancy tree-like structure to our output. Read on.

Make the following changes to your maven POM file to update or declare your maven-surefire-plugin configuration.

<plugin>
    <artifactId>maven-surefire-plugin</artifactId>
    <version>${maven-surefire-plugin.version}</version>
    <dependencies>
        <dependency>
            <groupId>me.fabriciorby</groupId>
            <artifactId>maven-surefire-junit5-tree-reporter</artifactId>
            <version>1.4.0</version>
        </dependency>
    </dependencies>
    <configuration>
        <reportFormat>plain</reportFormat>
        <consoleOutputReporter>
            <disable>true</disable>
        </consoleOutputReporter>
        <statelessTestsetInfoReporter
                implementation="org.apache.maven.plugin.surefire.extensions.junit5.JUnit5StatelessTestsetInfoTreeReporter">
                <theme>UNICODE</theme>
        </statelessTestsetInfoReporter>
    </configuration>
</plugin>

Eh voila.

> mvn test
...
[INFO] -------------------------------------------------------
[INFO]  T E S T S
[INFO] -------------------------------------------------------
[INFO] ├─ cf.dev.presentation.TreeReporterTest - 0.046 s
[INFO] │  └─ ✔ simpleTest - 0.010 s
[INFO] ├─ ── cf.dev.presentation.TreeReporterTest$NestedTests - 0.023 s
[INFO] │     ├─ ✔ simpleNestedParameterisedTest(String)[1] str=a - 0.004 s
[INFO] │     ├─ ✔ simpleNestedParameterisedTest(String)[2] str=b - 0 s
[INFO] │     └─ ✔ simpleNestedTest - 0.001 s
[INFO] 
[INFO] Results:
[INFO] 
[WARNING] Tests run: 4, Failures: 0, Errors: 0, Skipped: 0
[INFO] 

Real nice !

  • Tree structure of tests
  • Nested tests are children
  • Parameterized tests are shown as separate tests

But we can go one more if you have time? Lets start using the @DisplayName annotation to make those tree entries nicely formatted and using plain wording.

@DisplayName( "Surefire Tree Reporting Plugin Examples" )
class TreeReporterTest {

	@DisplayName( "A simple test at the top level" )
	@Test
	void simpleTest() {

	}

	@DisplayName( "A set of @Nested tests" )
	@Nested
	class NestedTests {

		@DisplayName( "A nested test" )
		@Test
		void simpleNestedTest() {

		}

		@DisplayName( "A nested and parameterised test " )
		@ParameterizedTest(name = "using the string value: {0}")
		@ValueSource(strings = {"a", "b"})
		void simpleNestedParameterisedTest(String str) {}

	}

}

Now we get some delightful test output.

> mvn test
...
[INFO] -------------------------------------------------------
[INFO]  T E S T S
[INFO] -------------------------------------------------------
[INFO] ├─ Surefire Tree Reporting Plugin Examples - 0.048 s
[INFO] │  └─ ✔ A simple test at the top level - 0.008 s
[INFO] ├─ ── Surefire Tree Reporting Plugin Examples A set of @Nested tests - 0.022 s
[INFO] │     ├─ ✔ A nested and parameterised test using the string value: a - 0.005 s
[INFO] │     ├─ ✔ A nested and parameterised test using the string value: b - 0.001 s
[INFO] │     └─ ✔ A nested test - 0.001 s
[INFO] 
[INFO] Results:
[INFO] 
[WARNING] Tests run: 4, Failures: 0, Errors: 0, Skipped: 0
[INFO]