Как включить глобальный тайм-аут для запуска тестового примера JUnit?

Этот вопрос предлагает использовать timeout аннотации @Test, чтобы JUnit принудительно останавливал тесты по истечении этого периода ожидания.

Но у нас уже есть около 5000 модульных тестов, и мы хотим установить политику, которая просит разработчиков никогда не выпускать тесты, для завершения которых требуется более 10 секунд. В политике, вероятно, будет указано «стремиться к ‹ 10 секунд», но тогда мы хотели бы гарантировать, что любой тест будет остановлен, скажем, через 30 секунд. (числа приведены только для примера, идея состоит в том, чтобы определить что-то, что «достаточно хорошо» для большинства случаев использования, но также гарантирует, что вещи не будут работать «вечно»)

Теперь мне интересно, есть ли способ включить такое поведение без перехода к каждому тестовому случаю и добавления этого параметра аннотации.

Существующий вопрос тоже не помогает: я ищу одно изменение, чтобы включить это, а не одно изменение для каждого тестового класса. Один центральный глобальный коммутатор. Не по одному на файл или метод.


person GhostCat    schedule 08.05.2018    source источник
comment
Вы можете добавить аннотацию @Rule (github.com/junit-team/junit4 /wiki/timeout-for-tests), который действителен для всего тестируемого класса, а не для отдельного метода. Похоже, у кого-то уже была ваша проблема: github.com/junit-team/junit4/ выпусков/140   -  person f1sh    schedule 08.05.2018
comment
@f1sh Что сокращает до одного изменения на файл, что все еще много, учитывая сотни тестовых классов. Это не то, о чем я просил.   -  person GhostCat    schedule 08.05.2018
comment
да. У меня нет другой идеи :/   -  person f1sh    schedule 08.05.2018
comment
Я не верю, что вы найдете один тайм-аут, чтобы управлять ими всеми, поскольку JUnit предназначен для небольших тестовых случаев, каждый случай имеет разное время выполнения. Но в худшем случае я бы выбрал решение @f1sh, это будет лучше изменить 100 файлов, чем 5000 методов. Через месяц или два вы сможете попросить студентов сделать это ;)   -  person AxelH    schedule 08.05.2018
comment
С помощью JUnit Jupiter (также известного как JUnit 5) можно зарегистрировать расширение глобально (т. е. с помощью механизма Java ServiceLoader), которое не пройдет тесты, выполнение которых занимает слишком много времени. Однако такое расширение не могло (в настоящее время) упреждающе завершить выполнение теста. Я мог бы опубликовать такой ответ, но поскольку вы утверждаете, что хотите, чтобы все не работало вечно... Я не уверен, что вам это будет интересно.   -  person Sam Brannen    schedule 08.05.2018
comment
@SamBrannen Я думаю, что это ближе к ответу, чем что-либо еще. И я быстрый сторонник, так почему бы вам просто не попробовать? ;)   -  person GhostCat    schedule 08.05.2018
comment
К вашему сведению: мы рассматриваем добавление поддержки глобальных тайм-аутов в JUnit Jupiter: github.com/junit-team/junit5/issues/80   -  person Sam Brannen    schedule 08.05.2018
comment
вероятно, хороший вопрос, но эти 10 секунд могут означать другую причину, а не фактическое время выполнения, что, если GC сработает на этой машине с событием остановки мира? просто говорю... я бы категорически не согласился с такими правилами IFF это было мое решение   -  person Eugene    schedule 08.05.2018
comment
@Eugene Правильное замечание, но я думаю, мы бы сделали несколько пробных прогонов, прежде чем действительно включить все это. И, конечно же, будьте довольно снисходительны к используемым числам.   -  person GhostCat    schedule 08.05.2018
comment
@GhostCat, значит, вы согласны жертвовать правильностью ради скорости? Не совсем так, а просто пытаюсь испортить вам вечеринку... :) Меня бы меньше заботило, насколько правильно написаны модульные тесты. эй я счастлив, если они присутствуют для начала!   -  person Eugene    schedule 08.05.2018
comment
@Eugene У нас есть около 5000 модульных тестов (методов) для нескольких миллионов строк производственного кода. Дело в том, что мы хотим включить полные модульные тесты для наших сборок проверки gerrit. Так что вы не можете зафиксировать изменения, которые нарушают модульный тест. Но затем мы должны убедиться, что эти проверочные сборки завершены в течение разумного периода времени. Конечно, в первую очередь надо набраться опыта. Но может быть полезно разрешить случайные сбои... путем применения таких строгих ограничений времени выполнения.   -  person GhostCat    schedule 08.05.2018
comment
@GhostCat - извините за поздний ответ. Я отправил ответ почти год назад, и модератор удалил мой пост. Надеюсь, кто-то получит пользу от моего расширенного повторного поста.   -  person Scott Babcock    schedule 08.01.2019
comment
Отвечает ли это на ваш вопрос? JUnit 5 - глобальный тайм-аут?   -  person Lubo    schedule 23.01.2020
comment
Ну, он отвечает за JUnit 5. Но мы все еще используем Junit 4. А варианты JUnit 5 были предложены и приняты уже в 2018 году.   -  person GhostCat    schedule 23.01.2020


Ответы (4)


Хотя JUnit Jupiter (то есть модель программирования и расширения, представленная в JUnit 5) еще не имеет встроенных поддержка глобальных тайм-аутов, вы по-прежнему можете реализовать поддержку глобальных тайм-аутов самостоятельно.

Единственная загвоздка в том, что расширение тайм-аута не может в настоящее время прерывать выполнение теста упреждающе. Другими словами, расширение времени ожидания в JUnit Jupiter в настоящее время может только отсчитывать время выполнения тестов, а затем генерировать исключение, если выполнение заняло слишком много времени (т. е. после ожидания завершения теста, что потенциально может никогда не произойти, если тест зависает).

В любом случае, если вы хотите реализовать расширение глобального тайм-аута без вытеснения для использования с JUnit Jupiter, вот что вам нужно сделать.

  1. Посмотрите на TimingExtension< /a> пример в Руководстве пользователя JUnit 5 для вдохновения. Вам понадобится аналогичный код, но вы захотите создать исключение, если duration превысит настроенное timeout. Как вы настраиваете свой глобальный тайм-аут, зависит от вас: жестко кодируйте его, ищите значение в системном свойстве JVM, ищите значение в пользовательской аннотации и т. д.
  2. Зарегистрируйте расширение глобального тайм-аута, используя механизм Java ServiceLoader. Подробнее см. в разделе Автоматическая регистрация расширения.

Удачного тестирования!

person Sam Brannen    schedule 08.05.2018
comment
Но TiminxExtension не показывает нам, какой обратный вызов использовать, текущие либо до выполнения, либо после, нам нужно что-то, что сработает во время выполнения теста, чтобы мы могли его прервать. - person Krzysztof Krasoń; 21.12.2018
comment
Пожалуйста, перечитайте мой ответ. TimingExtension демонстрирует, какие API расширений необходимо реализовать для поддержки неупреждающих тайм-аутов. - person Sam Brannen; 21.12.2018
comment
Как я уже сказал, расширение тайм-аута не может в настоящее время прерывать выполнение теста упреждающе. - person Sam Brannen; 21.12.2018
comment
Да, вы правы, я с нетерпением перешел к примеру, не прочитав ваш полный ответ. - person Krzysztof Krasoń; 21.12.2018
comment
Этот ответ не соответствует требованиям ОП. Почему он помечен как принятый ответ? - person Scott Babcock; 08.01.2019
comment
Потому что ОП написал это: « title = «как включить глобальный тайм-аут для запуска тестового примера junit»> stackoverflow.com/questions/50229133/ - person Sam Brannen; 08.01.2019
comment
Ах, хорошо ... Для людей, работающих с JUnit 4, я разместил ответ ниже, который обеспечивает глобальное упреждающее поведение тайм-аута, которое искал OP. - person Scott Babcock; 09.01.2019

Ознакомьтесь с моей библиотекой расширений JUnit 4 (https://github.com/Nordstrom/JUnit-Foundation). Среди функций, предоставляемых этой библиотекой, есть возможность определить глобальное значение тайм-аута, которое будет автоматически применяться к каждому методу тестирования, для которого еще не определен более длительный интервал тайм-аута.

Эта библиотека использует библиотеку генерации байтового кода Byte Buddy для установки обработчиков событий в стратегических точках потока выполнения теста JUnit 4. Глобальный тайм-аут применяется, когда JUnit создает экземпляр тестового класса для запуска «атомарного» теста.

Чтобы применить глобальное время ожидания, библиотека заменяет исходную аннотацию @Test объектом, реализующим интерфейс @Test. В этом подходе используются все встроенные функции тайм-аута JUnit, которые обеспечивают упреждающее завершение слишком продолжительных тестов. Использование встроенной функции тайм-аута устраняет необходимость инвазивной реализации или обработки особых случаев, и эта функция активируется без обращения к одному исходному файлу.

Все обновления, необходимые для установки и активации глобальной поддержки тайм-аута, находятся в файле проекта (POM/build.gradle) и дополнительном файле свойств. Интервал тайм-аута можно переопределить с помощью системного свойства, которое позволяет вносить коррективы из командной строки или программно. Для сценариев, в которых сбои времени ожидания вызваны временными условиями, вы можете связать функцию глобального времени ожидания с функцией автоматического повтора.

person Scott Babcock    schedule 07.01.2019
comment
Лично не пробовал, но звучит впечатляюще. Так что я дал вам +1. ;-) - person Sam Brannen; 09.01.2019
comment
Использование агента Java, однако, делает его немного менее привлекательным, но я полагаю, что альтернативы нет, учитывая архитектуру JUnit 4. - person Sam Brannen; 09.01.2019
comment
@sam-brannen Спасибо за голосование! Да, генерация байт-кода в агенте Java была единственным способом достижения моих целей в JUnit 4. Я бы не рекомендовал этот подход, если платформа все еще находилась в активной разработке, учитывая ее зависимость от недокументированного поведения и деталей реализации, которые не известны. не определяется общедоступным API. - person Scott Babcock; 10.01.2019

То, что вы, вероятно, ищете, не реализовано: https://github.com/junit-team/junit4/issues/140

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

Определите абстрактный родительский класс, например BaseIntegrationTest, со следующим полем @Rule:

public abstract class BaseIntegrationTest extends RunListener {

    private static final int TEST_GLOBAL_TIMEOUT_VALUE = 10;

    @Rule
    protected Timeout globalTimeout = Timeout.seconds(TEST_GLOBAL_TIMEOUT_VALUE);

}

Затем сделайте его родителем для каждого тестового класса в области видимости. Например:

public class BaseEntityTest extends BaseIntegrationTest {

    @Before
    public void init() {
        // init
    }

    @Test
    public void twoPlusTwoTest() throws Exception {
        assert 2 + 2 == 4;        
    }
}

Вот и все.

person Mikhail Kholodkov    schedule 08.05.2018
comment
По сути, это вариант реализации того, что предлагает другой существующий ответ. Голосую за усилия, но на самом деле не решают проблему введения глобального тайм-аута, не касаясь всех тестовых классов. И не заблуждайтесь: использование наследования (как-то) хуже, чем использование композиции... - person GhostCat; 08.05.2018
comment
Спасибо за комментарий. Ну, похоже, это единственное возможное решение, которое я нашел на данный момент. Если только команда JUnit не реализует некоторую настраиваемую переменную начальной загрузки в будущем. - person Mikhail Kholodkov; 08.05.2018

В настоящее время, возможно, вы не можете, потому что Junit 5 удалил Rule и заменил на Extension. приведенный выше пример не работает, поскольку пример кода реализует AfterTestExecutionCallback, который будет вызываться после выполнения тестового метода, поэтому тайм-аут бесполезен.

person Hieu Pham    schedule 15.09.2018
comment
JUnit Foundation работает только с JUnit 4. Я не изучал работу, связанную с его адаптацией к JUnit 5. - person Scott Babcock; 02.03.2019