在@After方法中检测Junit测试的失败或错误

时间:2011-08-08 12:12:25

标签: java junit

在JUnit中是否有办法在@After带注释的方法中检测测试用例中是否存在测试失败或错误?

一个丑陋的解决方案就是这样的:

boolean withoutFailure = false;

@Test
void test() {
  ...
  asserts...
  withoutFailure = true;
}

@After
public void tearDown() {
   if(!withoutFailuere) {
      this.dontReuseTestenvironmentForNextTest();
   }
}

这很难看,因为需要在测试代码中处理“基础设施”(没有失败标志)。

我希望我能在@After方法中获得测试状态!

4 个答案:

答案 0 :(得分:10)

如果您有幸使用JUnit 4.9或更高版本,TestWatcher将完全按照您的意愿行事。

分享并享受!

答案 1 :(得分:3)

我扩展了dsaff的答案,以解决TestRule无法在执行测试方法和后方法之间执行某些代码的问题。因此,使用简单的MethodRule,不能使用此规则来提供在@After带注释的方法中使用的成功标志。

我的想法是黑客!无论如何,它是使用TestRule(扩展TestWatcher)。 TestRule将获得有关测试失败或成功的知识。然后,我的TestRule将扫描该类以查找使用我的新AfterHack注释注释的所有方法,并使用成功标记调用该方法。

AfterHack注释

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;
    }
}

用法:

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() {}
}

顺便说一句:

  • 1)希望在Junit5中有更好的解决方案
  • 2)更好的方法是使用TestWatcher规则而不是@Before和@After方法(这就是我阅读dsaff的回答)

@see

答案 2 :(得分:2)

我认为您要做的是向JUnit核心添加RunListener。然后,您可以覆盖testFailure方法,将withoutFailure标记设置在一个位置,以便在@After带注释的方法中进行检查。

另请参阅:this blog post,以便在使用早期版本的JUnit时讨论此方法的一些问题。

答案 3 :(得分:1)

我不知道在@After方法中检测Junit测试失败的任何简单或优雅的方法。

如果可以使用TestRule而不是@After方法,可以使用两个链式TestRules,使用TestWatcher作为内部规则。

示例:

    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(){
      }

    }