Как объединить @Rule и @ClassRule в JUnit 4.12

Согласно примечаниям к выпуску 4.12, статические члены тестового класса можно аннотировать с помощью @Rule и @ClassRule:

статический член с аннотациями @Rule и @ClassRule теперь считается действительным. Это означает, что одно правило может использоваться для выполнения действий как до / после класса (например, настройка / удаление внешнего ресурса), так и между тестами (например, сброс внешнего ресурса),

Я хочу использовать эту функцию, чтобы инициализировать ресурс в начале всех тестов в файле, выполнять некоторую очистку ресурса между каждым тестом и избавляться от него после завершения всех тестов. Этот ресурс в настоящее время представлен классом, который расширяет ExternalResource.

В моем before а также after методы, как я могу провести различие между "до / после всех тестов" и "до / после каждого теста"? Нужно ли использовать другую / нестандартную реализацию TestRule чтобы сделать это?

4 ответа

Решение

Вы можете реализовать TestRule#apply и использовать isTest а также isSuite методы Description чтобы определить, какой Statement ваш TestRule применяется к.

Вот пример интерфейса, который вы могли бы построить, чтобы дать правило, которое имеет полный before, after, verify, beforeClass, afterClass, verifyClass Тип поведения:

public interface CombinedRule extends TestRule {
    default Statement apply(Statement base, Description description) {
        if (description.isTest()) {
            return new Statement() {
                public void evaluate() throws Throwable {
                    before();
                    try {
                        base.evaluate();
                        verify();
                    } finally {
                        after();
                    }
                }
            };
        }
        if (description.isSuite()) {
            return new Statement() {
                public void evaluate() throws Throwable {
                    beforeClass();
                    try {
                        base.evaluate();
                        verifyClass();
                    } finally {
                        afterClass();
                    }
                }
            };
        }
        return base;
    }

    default void before() throws Exception {
        //let the implementer decide whether this method is useful to implement
    }

    default void after() {
        //let the implementer decide whether this method is useful to implement
    }

    /**
     * Only runs for Tests that pass
     */
    default void verify() {
        //let the implementer decide whether this method is useful to implement
    }

    default void beforeClass() throws Exception {
        before();
    }

    default void afterClass() {
        after();
    }

    /**
     * Only runs for Suites that pass
     */
    default void verifyClass() {
        verify();
    }
}

Методы, отмеченные @Before а также @After будет выполняться до и после каждого теста, в то время как те, @BeforeClass а также @AfterClass будет выполняться до и после первого / последнего теста в классе соответственно.

Методы до / после @Rule выполняются до и после каждого теста, а методы до / после @ClassRule запускаются до / после всего тестового класса.

Вы можете использовать ExternalResource либо для @Rule или же @ClassRule до тех пор, пока методы-обработчики реагируют правильно для обоих сценариев. Насколько я могу судить из документации, нет никаких средств для разграничения двух категорий правил в методах класса правил. Если вы используете класс правил для обоих случаев, он будет применяться одинаково для обоих.

Вы не можете различить @BeforeClass а также @Before или же @AfterClass а также @After, Более подробную информацию о причине добавления этой функции можно найти в запросе.

Вы можете создать свое собственное правило, которое реализует оба TestRule а также MethodRule:

public SharableExternalResource implements TestRule, MethodRule {

  public final Statement apply(
      final Statement base, Description description) {
    return
        new Statement() {
          @Override
          public void evaluate() throws Throwable {
            beforeClass();

            List<Throwable> errors = new ArrayList<Throwable>();
            try {
                base.evaluate();
            } catch (Throwable t) {
                errors.add(t);
            } finally {
                try {
                    afterClass();
                } catch (Throwable t) {
                    errors.add(t);
                }
            }
            MultipleFailureException.assertEmpty(errors);
          }
        };
  }

  public final Statement apply(
      Statement base, FrameworkMethod method, Object target) {
    return
        new Statement() {
          @Override
          public void evaluate() throws Throwable {
            before();

            List<Throwable> errors = new ArrayList<Throwable>();
            try {
                base.evaluate();
            } catch (Throwable t) {
                errors.add(t);
            } finally {
                try {
                    after();
                } catch (Throwable t) {
                    errors.add(t);
                }
            }
            MultipleFailureException.assertEmpty(errors);
          }
        };
  }

  public void beforeClass() throws Exception {
    // do nothing
  }

  protected void before() throws Exception {
    // do nothing
  }

  protected void after() throws Exception {
    // do nothing
  }

  public void afterClass() throws Exception {
    // do nothing
  }
}
Другие вопросы по тегам