Подтвердить что ты не робот

Обнаружение сбоя или ошибки теста Junit в @После метода

Есть ли способ в JUnit обнаружить внутри @After аннотированный метод, если в тестовом случае произошел сбой или ошибка теста?

Одно уродливое решение будет примерно таким:

boolean withoutFailure = false;

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

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

Это некрасиво, потому что нужно обязательно следить за "инфраструктурой" (без флага флага) в тестовом коде.

Я надеюсь, что есть что-то, где я могу получить статус теста в методе @After!?

4b9b3361

Ответ 1

Если вам посчастливилось использовать JUnit 4.9 или новее, TestWatcher будет делать именно то, что вы хотите.

Поделитесь и наслаждайтесь!

Ответ 2

Я расширяю dsaff-ответ, чтобы решить проблему, что TestRule не может выполнить какой-либо код, сбрасываемый между выполнением тестового метода и методом after-method. Таким образом, с помощью простого MethodRule нельзя использовать это правило, чтобы обеспечить флаг успеха, который используется в аннотированных методах @After.

Моя идея - взломать! В любом случае, нужно использовать TestRule (extends TestWatcher). A 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() {}
}

BTW:

  • 1) Надеюсь, в Junit5 есть лучшее решение.
  • 2) Лучше всего использовать правило TestWatcher вместо @Before и @After Method (так я прочитал ответ dsaff)

@see

Ответ 3

Я думаю, что вам нужно будет добавить RunListener в ядро ​​JUnit. Затем вы можете переопределить метод testFailure, чтобы установить флаг withoutFailure в одном месте, чтобы вы могли проверить его в своем аннотированном методе @After.

Также см.: это сообщение в блоге для обсуждения некоторых проблем с этим подходом при использовании более ранних версий JUnit.

Ответ 4

Я не знаю простого или элегантного способа обнаружить сбой теста Junit в методе @After.

Если можно использовать 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(){
      }

    }

Ответ 5

RuleChain не работает. Метод @After все еще выполняется и завершается до того, как метод TestWatcher завершился с ошибкой. Это не помогает.

Мне также нужно найти способ сделать что-то сначала внутри @After или перед @After, если тест не пройден. Правило и все подходы TestWatcher здесь не работают.