Home > Software design >  How to wrap all junit tests?
How to wrap all junit tests?

Time:12-22

How can I create a junit5 extension/rule/interceptor that automatically wraps each @Test method.

Example: imagine measuring execution time:

class TimeExension {
    runTest() {
         StopWatch w = new StopWatch();
         w.start();

         //actually run the original test
         test.run();
 
         w.stop();
    }
}

How could this be applied to any test class?

CodePudding user response:

Update: for JUnit 5, you can use @ExtendWith annotation instead of Rules. For example, you can use the TimingExtension showed in the examples of JUnit5.

A full example (partially taken from the previously mentioned example):

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.AfterTestExecutionCallback;
import org.junit.jupiter.api.extension.BeforeTestExecutionCallback;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.api.extension.ExtensionContext;

import java.lang.reflect.Method;
import java.util.logging.Logger;

import static org.junit.jupiter.api.Assertions.assertEquals;

class TimingExtension implements BeforeTestExecutionCallback, AfterTestExecutionCallback {

    private static final Logger logger = Logger.getLogger(TimingExtension.class.getName());

    private static final String START_TIME = "start time";

    @Override
    public void beforeTestExecution(ExtensionContext context) throws Exception {
        getStore(context).put(START_TIME, System.currentTimeMillis());
    }

    @Override
    public void afterTestExecution(ExtensionContext context) throws Exception {
        Method testMethod = context.getRequiredTestMethod();
        long startTime = getStore(context).remove(START_TIME, long.class);
        long duration = System.currentTimeMillis() - startTime;

        logger.info(() ->
                String.format("Method [%s] took %s ms.", testMethod.getName(), duration));
    }

    private ExtensionContext.Store getStore(ExtensionContext context) {
        return context.getStore(ExtensionContext.Namespace.create(getClass(), context.getRequiredTestMethod()));
    }

}

@ExtendWith(TimingExtension.class)
class MyFirstJUnitJupiterTests {


    @Test
    void addition() {
        assertEquals(2, 1   1);
    }

}

In this case, the output will be something like this:

dec. 21, 2021 12:19:31 DU. TimingExtension afterTestExecution
INFO: Method [addition] took 6 ms.

Process finished with exit code 0

Old answer: In JUnit 4, for that purpose, you can use TestWatchers, Stopwatch with Rules. Here is some examples:

  • (1) is for timeout
  • (2) do something when the test passes or fails
  • (3) calculate execution time
import org.junit.Rule;
import org.junit.rules.TestWatcher;
import org.junit.rules.Timeout;
import org.junit.runner.Description;

class TestBase {
    @Rule
    public Timeout globalTimeout = Timeout.seconds(3);

    @Rule
    public TestWatcher watchman = new TestWatcher() {

        @Override
        protected void failed(Throwable e, Description description) {
            TestDescription td = description.getAnnotation(TestDescription.class);
            System.out.println("Failed: "   td.desc()[0]);

        }

    @Override
        protected void succeeded(Description description) {
            
        }


     @Rule
     public Stopwatch stopwatch = new Stopwatch() {
         @Override
         protected void succeeded(long nanos, Description description) {
             
         }

         @Override
         protected void failed(long nanos, Throwable e, Description description) {
             
         }

     };
}

CodePudding user response:

Probably InvocationInterceptor it the answer:

public class TestDurationReportExtension implements InvocationInterceptor {
    @Override
    public void interceptTestMethod(Invocation<Void> invocation,
            ReflectiveInvocationContext<Method> invocationContext,
            ExtensionContext extensionContext) throws Throwable {
 
        long beforeTest = System.currentTimeMillis();
        try {
            invocation.proceed();
        } finally {
            long afterTest = System.currentTimeMillis();
            long duration = afterTest - beforeTest;
             
            String testClassName = invocationContext.getTargetClass().getSimpleName();
            String testMethodName = invocationContext.getExecutable().getName();
            System.out.println(String.format("%s.%s: %dms", testClassName, testMethodName, duration));
        }
    }
}

Use with:

@ExtendWith(TestDurationReportExtension.class)
public class DemoTest { .. }

Idea from: https://www.mscharhag.com/java/junit5-custom-extensions

  • Related