Detect Failure or Error of Junit Test in @After method

后端 未结 4 1622
你的背包
你的背包 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:16

    If you are lucky enough to be using JUnit 4.9 or later, TestWatcher will do exactly what you want.

    Share and Enjoy!

    0 讨论(0)
  • 2021-01-01 10:20

    I think what you'll want to do is add a RunListener to the JUnit core. You can then override the testFailure method to set your withoutFailure flag in a single place so you can check it in your @After annotated method.

    Also see: this blog post for a discussion of some issues with this approach when using earlier versions of JUnit.

    0 讨论(0)
  • 2021-01-01 10:23

    I don't know any easy or elegant way to detect the failure of a Junit test in an @After method.

    If it is possible to use a TestRule instead of an @After method, one possibility to do it is using two chained TestRules, using a TestWatcher as the inner rule.

    Example:

        package org.example;
    
        import static org.junit.Assert.fail;
    
        import org.junit.Rule;
        import org.junit.Test;
        import org.junit.rules.ExternalResource;
        import org.junit.rules.RuleChain;
        import org.junit.rules.TestRule;
        import org.junit.rules.TestWatcher;
        import org.junit.runner.Description;
    
        public class ExampleTest {
    
          private String name = "";
          private boolean failed;
    
          @Rule
          public TestRule afterWithFailedInformation = RuleChain
            .outerRule(new ExternalResource(){
              @Override
              protected void after() {
                System.out.println("Test "+name+" "+(failed?"failed":"finished")+".");
              }      
            })
            .around(new TestWatcher(){
              @Override
              protected void finished(Description description) {
                name = description.getDisplayName();
              }
              @Override
              protected void failed(Throwable e, Description description) {
                failed = true;
              }      
            })
          ;
    
          @Test
          public void testSomething(){
            fail();
          }
    
          @Test
          public void testSomethingElse(){
          }
    
        }
    
    0 讨论(0)
  • 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<Method> getAfterHackMethods(Class<?> testClass) {
            List<Method> 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

    0 讨论(0)
提交回复
热议问题