TIL: Trim your stacktraces with Maven Surefire

As part of this years Hacktoberfest we at Info Support decided to put some effort in resolving issues for the popular open-source projects we love. One of those projects is Maven and its plugins. Today we proposed an improved implementation which makes the output of your failing test more valuable.

If you are a Maven user, you probably know Maven Surefire. A widely used Maven plugin to execute your tests. You get a handy overview of the results of your tests set: number of successful tests, failed tests and erroneous tests.

When you scroll down the output, you have probably observed the gigantic stacktraces of your failing tests. Including but not limited to internal method calls of your test runner.

Today we discovered a very useful feature to limit those colossal stacktrace: trimStackTrace. This greatly reduces the footprint of your build output by leaving out those weird internal stacktrace lines.

To use this feature you can either use -DtrimStackTrace=true or configure the Surefire Plugin in your pom.xml:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <configuration>
        <trimStackTrace>true</trimStackTrace>
    </configuration>
</plugin>

This reduces your stacktrace from 60 lines to only 1 single line:

[ERROR] nl.infosupport.Test.testInnerClassException  Time elapsed: 0.001 s  <<< ERROR!
java.lang.RuntimeException: Oh noes! Exception thrown here..
        at nl.infosupport.Foo.bar(Foo.java:10)
        at nl.infosupport.Test.testInnerClassException(Test.java:9)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:568)
        at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:628)
        at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:117)
        at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$7(TestMethodTestDescriptor.java:184)
        at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
        at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:180)
        at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:127)
        at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:68)
        at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:135)
        at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
        at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
        at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
        at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
        at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
        at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
        at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
        <removed 33 lines>        
        at org.apache.maven.surefire.booter.ForkedBooter.run(ForkedBooter.java:595)
        at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:581)
[ERROR] nl.infosupport.Test.testInnerClassException Time elapsed: 0 s <<< ERROR!
java.lang.RuntimeException: Oh noes! Exception thrown here..
at nl.infosupport.Test.testInnerClassException(Test.java:9)

That’s convenient, isn’t it?

If you made it across this enormous stacktrace I have got even better news! Following issue SUREFIRE-1887, we propose an improvement of this feature. Check out our pull-request at Github.

With this implementation a trimmed stacktrace not only consist of the element(s) in the test class, but extends the stacktrace to include methods calls leading up to the test method.

[ERROR] nl.infosupport.Test.testInnerClassException Time elapsed: 0 s <<< ERROR!
java.lang.RuntimeException: Oh noes! Exception thrown here..
at nl.infosupport.Foo.bar(Foo.java:10)
at nl.infosupport.Test.testInnerClassException(Test.java:9)

Now you know the method that throws the error is bar() at Foo. This trims your stacktrace, while preserving those precious stacktrace lines.

So go ahead, use trimStackTrace! It saves you valuable time, that you can spend on writing great code.