Detect Failure or Error of Junit Test in @After method

后端 未结 4 1633
你的背包
你的背包 2021-01-01 09:50

Is there a way in JUnit to detect within an @After annotated method if there was a test failure or error in the test case?

One ugly solution would b

4条回答
  •  北海茫月
    2021-01-01 10:30

    I extend dsaff's answer to solve the problem that a TestRule can not execute some code snipped between the execution of the test-method and the after-method. So with a simple MethodRule one can not use this rule to provide a success flag that is use in the @After annotated methods.

    My idea is a hack! Anyway, it is to use a TestRule (extends TestWatcher). A TestRule will get knowledge about failed or success of a test. My TestRule will then scan the class for all Methods annotated with my new AfterHack annotations and invoke that methods with a success flag.


    AfterHack annotation

    import static java.lang.annotation.ElementType.METHOD;
    import static java.lang.annotation.RetentionPolicy.RUNTIME;    
    import java.lang.annotation.Retention;
    import java.lang.annotation.Target;
    
    @Retention(RUNTIME)
    @Target(METHOD)
    public @interface AfterHack {}
    

    AfterHackRule

    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    import java.util.ArrayList;
    import java.util.List;
    
    import org.junit.rules.TestWatcher;
    import org.junit.runner.Description;
    
    public class AfterHackRule extends TestWatcher {
    
        private Object testClassInstance;
        public AfterHackRule(final Object testClassInstance) {
            this.testClassInstance = testClassInstance;
        }
    
        protected void succeeded(Description description) {
            invokeAfterHackMethods(true);
        }
    
        protected void failed(Throwable e, Description description) {
            invokeAfterHackMethods(false);
        }
    
        public void invokeAfterHackMethods(boolean successFlag) {
            for (Method afterHackMethod :
                        this.getAfterHackMethods(this.testClassInstance.getClass())) {
                try {
                    afterHackMethod.invoke(this.testClassInstance, successFlag);
                } catch (IllegalAccessException | IllegalArgumentException
                         | InvocationTargetException e) {
                    throw new RuntimeException("error while invoking afterHackMethod " 
                              + afterHackMethod);
                }
            }
        }
    
        private List getAfterHackMethods(Class testClass) {
            List results = new ArrayList<>();            
            for (Method method : testClass.getMethods()) {
                if (method.isAnnotationPresent(AfterHack.class)) {
                    results.add(method);
                }
            }
            return results;
        }
    }
    

    Usage:

    public class DemoTest {
    
        @Rule
        public AfterHackRule afterHackRule = new AfterHackRule(this);
    
        @AfterHack
        public void after(boolean success) { 
            System.out.println("afterHack:" + success);
        }
    
        @Test
        public void demofails() {
            Assert.fail();
        }
    
        @Test
        public void demoSucceeds() {}
    }
    

    BTW:

    • 1) Hopefully there is a better solution in Junit5
    • 2) The better way is to use the TestWatcher Rule instead of the @Before and @After Method at all (that is the way I read dsaff's answer)

    @see

提交回复
热议问题