Search code examples
javajunitappium

JUnit how to save DisplayName in xml report file


I want to generate XML report file with info of executed tests. Previously I used shown below @Rule in JUnit4 but now I want to display comment or some short of short description instead of testName. After some research I found @DisplayNamein JUnit5 that seems to suit my needs. Now I have to update my @Rule to display @DisplayNamecontent instead of testName[number].className. Tests are written in JUnit classes and have following structure

@Test
@DisplayName("This text should be displayed as name")
public void testy1() {...}

To display outcome and name of run test I created new rule with following scheme

@Rule
public TestRule watchman = new TestWatcher() {
    @Override
    public Statement apply(Statement base, Description description) {
                return super.apply(base, description);
            }
@Override
protected void succeeded(Description description) {
        try {

            junitWriter.write("\n   <test>"
                            + "\n       <name>" + description.getDisplayName() + "</name>"
                            + "\n       <result>..
                            + "\n       <message>..
                            + "\n   </test>");
        } catch [..]
    }
@Override
        protected void failed(Throwable e, Description description) {
            //same scheme with error
        }
}

To start tests I run

Result result = JUnitCore.runClasses(mainSuiteClassCreatingReport);
System.out.printf("Test ran: %s, Failed: %s%n", result.getRunCount(), result.getFailureCount());

In generated XML file instead of "DisplayName" annotation I keep getting methodName[number].className. Can anybody help me to display/save it the way I want?


Solution

  • You cannot use @DisplayName with JUnit 4.

    If you want to use @DisplayName, you'll need to switch to JUnit Jupiter (a.k.a., JUnit 5) and implement an Extension.

    Note that JUnit Jupiter does not yet support an extension API like the TestWatcher rule from JUnit 4; however, such support is coming in JUnit 5.4: https://github.com/junit-team/junit5/issues/542

    To customize what is returned from getDisplayName() in Description in JUnit 4, you would need to write your own custom Runner. As an alternative, you could simply parse the value returned from getDisplayName() and use that to generate your own display name.

    Or, if you want to support your own custom display name annotation with JUnit 4, such as this:

    package foo;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface MyDisplayName {
    
        String value();
    
    }
    

    You could look it up in your TestWatcher implementation using reflection like this:

    package foo;
    
    import java.lang.reflect.Method;
    
    import org.junit.Rule;
    import org.junit.Test;
    import org.junit.rules.TestRule;
    import org.junit.rules.TestWatcher;
    import org.junit.runner.Description;
    
    public class ExampleTests {
    
        @Rule
        public TestRule watchman = new TestWatcher() {
    
            @Override
            protected void succeeded(Description description) {
                System.out.println("Display name: " + getDisplayName(description));
            }
    
            private String getDisplayName(Description description) {
                Class<?> testClass = description.getTestClass();
                String methodName = description.getMethodName();
                try {
                    Method method = testClass.getDeclaredMethod(methodName);
                    MyDisplayName myDisplayName = method.getAnnotation(MyDisplayName.class);
                    return myDisplayName.value();
                }
                catch (Exception ex) {
                    // do something with the exception if you want...
                }
                // default:
                return testClass.getName() + "." + methodName + "()";
            }
        };
    
        @Test
        public void testWithoutCustomDisplayName() {
        }
    
        @Test
        @MyDisplayName("Custom display name!")
        public void testWithCustomDisplayName() {
        }
    
    }
    

    Running that test class outputs the following:

    Display name: foo.ExampleTests.testWithoutCustomDisplayName()
    Display name: Custom display name!