Log or Display on console names of test method executed by Junit5!

Log or Display on console names of test method executed by Junit5!

Easy way of logging in Junit5.

As you are making applications cloud-native and 12 factor-based app development. Unit testing and automation testing is now the norm in the development cycle. First, write down unit test cases and then start the development, or in standard words, it's TDD and or BDD.

If you worked on Juni4 then you should have used the SOP (System.out.println(); )statement to print the control is in which method, this is nothing like some complex logic its a very simple thing just you need to in logs what is the class name and method name. How we can achieve this using Juni5?

In this post first I demonstrate using logger or SOP statement how you can add logs and that helps to debug and a second way which is juni5 way to log.

Junit4 Code Snippet:


public class CalciTest {



    @Test
    public void testAdd() {
        System.out.println("testAdd");
        throw new NullPointerException("testAdd exception");
    }

    @Test
    @DisplayName("display name for testSubstract")
    public void testSubstract() {
        System.out.println("testSubstract");
    }

    @Test
    @Disabled("multiple disabled for demo")
    public void testMultiple() {
        System.out.println("testMultiple");
    }
}

In the above snippet, if you have to do any changes in logging or display name changes, you have to modify the test cases code and update that particular message, it is more like a hardcoded or not loosely coupled one.

In the above snippet, I have annotated a method with @DisplayName this will display the method name but logging or displaying the method is the cross-cutting job it's not the main work.

Let us check the junit5 code snippet.

JUnit 5 interface TestExecutionListener defines 8 methods that are called by JUnit at specific steps of test execution. To print the names of tests methods I needed to implement only two methods — executionStarted​(TestIdentifier) and executionFinished​(TestIdentifier, TestExecutionResult)


public class LoggingExecutionListener implements TestExecutionListener {
public Description toDescription(TestIdentifier i) {
        Description d = new Description(i.getDisplayName());
        TestSource ts = i.getSource().orElse(null);
        if (ts instanceof MethodSource m) {
            d.setClassName(m.getJavaClass());
            d.setMethodName(m.getMethodName());
        }
        return d;
    }
public String shorterStacktrace(Throwable ex) {
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        ex.printStackTrace(new PrintStream(os));
        return os.toString(StandardCharsets.UTF_8).lines().limit(6).collect(Collectors.joining("\n")) + "\n\t...";
    }
@Override
    public void executionStarted(TestIdentifier i) {
        Description d = toDescription(i);
        if (d.getMethodName() != null) { 
            log(">" + d);
        }
    }
@Override
    public void executionFinished(TestIdentifier i, TestExecutionResult r) {
        Description d = toDescription(i);
        if (d.getMethodName() != null) { 
            if (r.getThrowable().isPresent()) {
                log(shorterStacktrace(r.getThrowable().get()));
            }
            log("<" + d + "\n");
        }
    }
void log(String message) {
        System.out.println("Message ***:: " + message);
    }
}

Register an instance of this TestExecutionListener class with a Launcher to be notified of events that occur during test execution. All methods in this class have empty default implementations. Concrete implementations may therefore override one or more of these methods to be notified of the selected events.

Junit5 provides below two implementations:

  1. LoggingListener
  2. SummaryGeneratingListener

Let us check our code now.


public class Description {

String displayName, className, methodName;

public Description(String displayName) {
        this.displayName = displayName;
    }
public String getClassName() {
        return className;
    }
public void setClassName(Class<?> className) {
        this.className = className.getSimpleName();
    }
public String getMethodName() {
        return methodName;
    }
public void setMethodName(String methodName) {
        this.methodName = methodName + "()";
    }

@Override
    public String toString() {
        String msg = className + "." + methodName;
        if (!displayName.equals(methodName)) {
            msg += " - " + displayName;
        }
return msg;
    }
}

To load implementations of TestExecutionListener, JUnit 5 uses ServiceLoader. For LoggingExecutionListener to be recognized by JUnit, it has to be marked as a service provider. It is done by adding a text file named org.junit.platform.launcher.TestExecutionListener to /META-INF/services/ folder of the jar. File org.junit.platform.launcher.TestExecutionListener contains one line — the name of the class implementing TestExecutionListener:

com.techwasti.test.listener.LoggingExecutionListener

This is the one way to demonstrate how you can go with customization or else you can use JUnit provided implementation class LoggingListener or SummaryGeneratingListener.

More such articles:

https://medium.com/techwasti

https://www.youtube.com/channel/UCiTaHm1AYqMS4F4L9zyO7qA

https://www.techwasti.com/

==========================**=========================

If this article adds any value for you then please clap and comment.

Let’s connect on Stackoverflow, LinkedIn, & Twitter.

Did you find this article valuable?

Support techwasti by becoming a sponsor. Any amount is appreciated!