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

Объединение @ClassRule и @Rule в JUnit 4.11

В JUnit 4.10 и ниже можно аннотировать правило как и @Rule, так и @ClassRule. Это означает, что правило вызывается перед классом до/после класса и до/после каждого теста. Одна из возможных причин для этого - установить дорогостоящий внешний ресурс (через вызовы @ClassRule), а затем дешево reset он (через вызовы @Rule).

Как и в случае JUnit 4.11, поля @Rule должны быть нестатическими, а поля @ClassRule должны быть статическими, поэтому выше это невозможно.

Есть очевидные обходные пути (например, явно разделять обязанности @ClassRule и @Rule на отдельные правила), но, похоже, стыдно иметь мандат на использование двух правил. Я кратко рассмотрел использование @Rule и вывод о том, является ли это первым/последним тестом, но я не считаю, что информация доступна (по крайней мере, она не доступна непосредственно в описании).

Есть ли опрятный и аккуратный способ комбинирования функций @ClassRule и @Rule в одном правиле в JUnit 4.11?

Спасибо, Роуэн

4b9b3361

Ответ 1

Как и в случае JUnit 4.12 (невыпущенный во время записи), можно будет аннотировать одно статическое правило как с @Rule, так и @ClassRule.

Обратите внимание, что он должен быть статичным: нестатическое правило, аннотированное с помощью @Rule и @ClassRule, по-прежнему считается недействительным (поскольку что-либо аннотированное @ClassRule работает на уровне класса, так что только действительно имеет смысл как статический член).

Смотрите примечания к выпуску и мой запрос на извлечение, если вас интересует более подробная информация.

Ответ 2

Другим возможным обходным путем является объявление статического @ClassRule и использование этого значения при объявлении нестатического @Rule тоже:

@ClassRule
public static BeforeBetweenAndAfterTestsRule staticRule = new BeforeBetweenAndAfterTestsRule();
@Rule
public BeforeBetweenAndAfterTestsRule rule = staticRule;

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

Ответ 3

Еще одно возможное обходное решение - объявить нестатический @Rule и заставить его действовать на статических соавторах: если соавтор еще не инициализирован, @Rule знает, что он работает в первый раз (так что он может настроить его сотрудников, например, запуск внешнего ресурса); если они инициализированы, @Rule может выполнять эту работу для каждого теста (например, перепродать внешний ресурс).

Это имеет недостаток, который @Rule не знает, когда он обработал последний тест, поэтому не может выполнять какие-либо действия после класса (например, переустанавливать внешний ресурс); однако вместо этого можно использовать метод @AfterClass.

Ответ 4

Ответ на ваш вопрос следующий: нет чистого способа сделать это (установите только два правила одновременно). Мы попытались реализовать аналогичную задачу для повторных попыток автоматических тестов, и два правила были объединены (для этой задачи), и такой уродливый подход был реализован: Тесты повторяются с двумя правилами

Но если более точно подумать над необходимой задачей (которая должна быть реализована), то лучший подход можно использовать с помощью jUnit custom Runner:  Retry Runner.

Итак, для лучшего подхода вам будет полезно узнать ваш конкретный вариант использования.

Ответ 5

У меня возникли аналогичные проблемы, есть две работы. Мне не нравится ни один, но у них разные компромиссы:

1) Если ваше правило выдает методы очистки, вы можете вручную вызвать очистку внутри метода @Before.

@ClassRule
public static MyService service = ... ;

@Before
public void cleanupBetweenTests(){
     service.cleanUp();
}

Недостатком этого является то, что вам нужно помнить (и рассказывать другим в своей команде), чтобы всегда добавлять этот метод @Before или создавать абстрактный класс, который ваши тесты наследуют, чтобы выполнить очистку для вас.

2) Имейте 2 поля, один статический, один нестатический, указывающий на один и тот же объект, каждое поле, аннотированное либо a @ClassRule, либо @Rule соответственно. Это необходимо, если очистка не открывается. Конечно, недостатком является то, что вы также должны помнить, что и @ClassRule, и @Rule указывают на то, что выглядит странно.

 @ClassRule
 public static MyService service = ... ;

@Rule
public MyService tmp = service ;

Затем в вашей реализации вы должны различать тестовый набор или один тест. Это можно сделать, проверив, есть ли у Description дети. В зависимости от этого вы создаете различные адаптеры Statement для обработки очистки или нет:

@Override
protected void after() {

    //class-level shut-down service
    shutdownService();
}


@Override
protected void before() {

    //class-level init service
    initService();
}

@Override
public Statement apply(Statement base, Description description) {

        if(description.getChildren().isEmpty()){
            //test level perform cleanup

            return new CleanUpStatement(this,base);
        }
        //suite level no-change
        return super.apply(base, description);

    }

Вот пользовательский класс Statement для очистки перед каждым тестом:

private static final class CleanUpStatement extends Statement{
        private final MyService service;

        private final Statement statement;



        CleanUpStatement(MyService service, Statement statement) {
            this.service = service;
            this.statement = statement;
        }



        @Override
        public void evaluate() throws Throwable {
            //clear messages first
            myService.cleanUp();
            //now evaluate wrapped statement
            statement.evaluate();
        }

    }

После всего этого я бы больше склонялся к варианту 1, поскольку он больше раскрывает намерения и меньше кода для поддержки. Я также беспокоюсь о том, что другие пытаются изменить код в варианте 2, думая, что есть ошибка, поскольку одно и то же поле указано дважды. Дополнительные усилия, комментарии кодов и т.д. Не стоят того.

В то же время у вас все еще есть плита котла для копирования и вставки повсюду или абстрактных классов с использованием метода шаблона.