Простой способ запускать один и тот же тест junit снова и снова?

123

Как говорится в названии, я ищу простой способ запускать тесты JUnit 4.x несколько раз подряд автоматически с помощью Eclipse.

Например, один и тот же тест будет выполняться 10 раз подряд и сообщать результат.

У нас уже есть сложный способ сделать это, но я ищу простой способ сделать это, чтобы быть уверенным, что нестабильный тест, который я пытался исправить, остается исправленным.

Идеальным решением будет плагин / настройка / функция Eclipse, о которых я не знаю.

Стефан Тиберг
источник
5
Мне очень любопытно, зачем вам это нужно.
Buhb
Я провожу большой тест черного ящика, внес небольшие изменения и хочу посмотреть, как это повлияло на стабильность этого ранее нестабильного теста.
Стефан Тайберг,
Это действительно так, за исключением того, что вы хотите, чтобы он работал до отказа, а я просто хочу запустить его несколько раз, что может повлиять на ответы, которые я получаю.
Стефан Тиберг,
4
Вы против TestNG, потому что в противном случае вы можете просто использовать @Test (invocationCount = 10), и это все, что вам нужно.
Роберт Массайоли
1
Я не был «против» TestNG, просто мы не использовали его в этом проекте.
Стефан Тайберг

Ответы:

124

Самый простой (как минимум необходимый объем нового кода) способ сделать это - запустить тест как параметризованный (пометить @RunWith(Parameterized.class)и добавить метод для предоставления 10 пустых параметров). Таким образом, фреймворк выполнит тест 10 раз.

Этот тест должен быть единственным тестом в классе, или, лучше сказать, все методы тестирования должны выполняться в классе 10 раз.

Вот пример:

@RunWith(Parameterized.class)
public class RunTenTimes {

    @Parameterized.Parameters
    public static Object[][] data() {
        return new Object[10][0];
    }

    public RunTenTimes() {
    }

    @Test
    public void runsTenTimes() {
        System.out.println("run");
    }
}

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

Если вы реализуете свой собственный бегун, то вы можете запустить тест 10 раз. Если вы используете сторонний бегун, то с 4.7 вы можете использовать новую @Ruleаннотацию и реализовать MethodRuleинтерфейс так, чтобы он принимал оператор и выполнял его 10 раз в цикле for. Текущий недостаток такого подхода в том, что @Beforeи @Afterзапускается только один раз. Скорее всего, это изменится в следующей версии JUnit ( @Beforeбудет запускаться после @Rule), но независимо от того, что вы будете действовать с одним и тем же экземпляром объекта (что не относится к Parameterizedбегуну). Это предполагает, что любой бегун, с которым вы запускаете класс, правильно распознает @Ruleаннотации. Это только в том случае, если он делегируется исполнителям JUnit.

Если вы работаете с настраиваемым бегуном, который не распознает @Ruleаннотацию, то вам действительно приходится писать собственный бегун, который надлежащим образом делегирует этому бегуну и запускает его 10 раз.

Обратите внимание, что есть и другие способы решения этой проблемы (например, бегун Theories), но все они требуют бегуна. К сожалению, JUnit в настоящее время не поддерживает уровни бегунов. Это бегун, который сковывает других бегунов.

Ишай
источник
2
К сожалению, я уже использую @RunWith с другим бегуном, но в противном случае это было бы идеальным решением.
Стефан Тайберг,
Да, это решение, которое я хотел бы иметь, и которое будет лучшим для большинства людей, поэтому я собираюсь пойти дальше и принять ответ.
Стефан Тиберг, 01
Для альтернативного и, возможно, менее хакерского решения см .: stackoverflow.com/a/21349010/281545
Mr_and_Mrs_D
Хорошее решение! Я получил исключение, сообщающее мне, что метод данных должен возвращать Iterable of Arrays. Я исправил это соответствующим образом: @ Parameterized.Parameters public static Iterable <Object []> data () {return Arrays.asList (new Object [20] [0]); }
nadre 09
1
Не могли бы вы дать ссылку на этот ответ для JUnit 5? В нем описывается запрошенная функция, которая была добавлена ​​в JUnit 5
Marcono1234,
102

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

введите описание изображения здесь

когда вы запустите тест, intellij выполнит все выбранные вами тесты указанное вами количество раз.

Пример выполнения 624 тестов 10 раз: введите описание изображения здесь

smac89
источник
4
Это идеально, теперь , если вы можете указать на затмение способ сделать это, что бы ответить на вопрос OP к точке
Хал
Полагаться на конкретный инструмент для размещения фактической логики или требований - это анти-шаблон.
Mickael
1
@Mickael Повторение теста N раз обычно не является требованием тестирования. Фактически тесты должны быть детерминированными, чтобы независимо от того, сколько раз они повторяются, они всегда должны давать один и тот же результат. Можете ли вы объяснить антипаттерн, о котором говорите?
smac89
Если повторение теста было полезно для одного разработчика, вероятно, будет полезно и для других. Таким образом, если среда выполнения теста и код могут содержать логику для включения повторения, она должна быть предпочтительнее, поскольку она позволяет факторизовать усилия и решение и позволяет участникам использовать инструмент по желанию с тем же результатом. Размещение многоразовой логики в области IDE / разработчика, когда ее можно вставить в код, - это своего рода недостающая факторизация.
Mickael
68

Я обнаружил, что аннотация повторения Spring полезна для таких вещей:

@Repeat(value = 10)

Последний документ (Spring Framework 4.3.11.RELEASE API):

Лора
источник
46
Я бы не назвал простой способ изменить среду тестирования.
Стефан Тайберг,
3
Вам не нужно менять структуру тестирования - она ​​отлично работает с JUnit. Главный недостаток в том, что JUnit все еще рассматривает его как единый тест. Так что в первый раз, когда он сломается, выполнение остановится. Однако, если вы еще не используете Spring, то, вероятно, это не тот путь, по
которому
Похоже, у меня не работает (Spring 4.3.6 через Spring Boot 1.5.1)
Дэвид Тонхофер,
Не работаю для меня с пружинными загрузками 2.1.6 и JUnit 5
jo-
Отлично работает с весенней загрузкой 2. Не забудьте добавить @RunWith (SpringRunner :: class), как указано на плакате «Модульное тестирование весной»!
Агостон Хорват
33

На основе следующих ресурсов:

пример

Создайте и используйте @Repeatаннотацию следующим образом:

public class MyTestClass {

    @Rule
    public RepeatRule repeatRule = new RepeatRule();

    @Test
    @Repeat(10)
    public void testMyCode() {
        //your test code goes here
    }
}

Repeat.java

import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.METHOD;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention( RetentionPolicy.RUNTIME )
@Target({ METHOD, ANNOTATION_TYPE })
public @interface Repeat {
    int value() default 1;
}

RepeatRule.java

import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;

public class RepeatRule implements TestRule {

    private static class RepeatStatement extends Statement {
        private final Statement statement;
        private final int repeat;    

        public RepeatStatement(Statement statement, int repeat) {
            this.statement = statement;
            this.repeat = repeat;
        }

        @Override
        public void evaluate() throws Throwable {
            for (int i = 0; i < repeat; i++) {
                statement.evaluate();
            }
        }

    }

    @Override
    public Statement apply(Statement statement, Description description) {
        Statement result = statement;
        Repeat repeat = description.getAnnotation(Repeat.class);
        if (repeat != null) {
            int times = repeat.value();
            result = new RepeatStatement(statement, times);
        }
        return result;
    }
}

PowerMock

Использование этого решения с @RunWith(PowerMockRunner.class)требует обновления до Powermock 1.6.5 (который включает патч ).

Р. Остерхолт
источник
Да. Как вы проводите тесты?
R. Oosterholt
Я сам не использую eclipse. Может вы не пользуетесь тест-раннером junut 4? (см. документ «Настройка тестовой конфигурации» )
Р. Остерхолт,
29

С JUnit 5 я смог решить эту проблему с помощью аннотации @RepeatedTest :

@RepeatedTest(10)
public void testMyCode() {
    //your test code goes here
}

Обратите внимание, что @Testаннотацию не следует использовать вместе с @RepeatedTest.

Сезар Альберка
источник
Звучит очень многообещающе, просто отметим, что релизной версии пока нет.
9ilsdx 9rvj 0lo
1
JUnit 5 попал в GA пару недель назад.
mkobit
11

Что-то не так с:

@Test
void itWorks() {
    // stuff
}

@Test
void itWorksRepeatably() {
    for (int i = 0; i < 10; i++) {
        itWorks();
    }
}

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

Не нужно делать в конфигурации или аннотации то, что можно делать в коде.

Сору
источник
2
Я хотел бы запустить несколько тестов как обычные модульные тесты и получить трассировку и статус каждого из них.
Стефан Тайберг 01
24
В этом случае "@Before" s и "@After" s не будут запускаться
Богдан
3
Это наряду с ручным вызовом @Beforeаннотированного метода до itWorks() решения моей проблемы.
Жуан Невес,
Вы знаете концепцию DRY? en.wikipedia.org/wiki/Don%27t_repeat_yourself Я рекомендую произвести некоторую настройку вместо того, чтобы копировать и вставлять ваш цикл повсюду.
Kikiwa
Очередь редактирования этого ответа заполнена; поэтому я помещу это в комментарий: для JUnit4 тесты должны быть общедоступными.
Ричард Джессоп,
7

Для меня это работает намного проще.

public class RepeatTests extends TestCase {

    public static Test suite() {
        TestSuite suite = new TestSuite(RepeatTests.class.getName());

        for (int i = 0; i < 10; i++) {              
        suite.addTestSuite(YourTest.class);             
        }

        return suite;
    }
}
Qualk
источник
Потрясающе, поскольку не использует другой фреймворк и фактически работает с JUnit 3 (важно для Android)
Владимир Иванов
1
Реализация с JUnit4 может быть выполнена с помощью Runner: public class RepeatRunner extends BlockJUnit4ClassRunner { public RepeatRunner(Class klass) throws InitializationError { super(klass); } @Override public void run(final RunNotifier notifier) { for (int i = 0; i < 10; i++) { super.run(notifier); } } }хотя, по крайней мере, в подключаемом модуле Eclipse JUnit вы получаете такие результаты, как: «10/1 тесты пройдены»
Питер Випперманн,
7

В библиотеке tempus-fugit есть аннотация Intermittent, которая работает с JUnit 4.7, @Ruleчтобы повторить тест несколько раз или с @RunWith.

Например,

@RunWith(IntermittentTestRunner.class)
public class IntermittentTestRunnerTest {

   private static int testCounter = 0;

   @Test
   @Intermittent(repition = 99)
   public void annotatedTest() {
      testCounter++;
   }
}

После запуска теста (с IntermittentTestRunner в @RunWith) testCounterбудет равно 99.

Тоби
источник
Да, здесь та же проблема, ведь уже используется другой бегун и поэтому нельзя использовать его, хотя хорошая идея.
Стефан Тиберг,
Да, у меня такая же проблема с RunWith ... как оказалось, я немного поправил tempus-fugit, чтобы немного обойти это, вы можете использовать @Rule вместо runner, когда хотите запускать повторно. Вы помечаете его @Repeating вместо прерывистого. Однако версия правила не будет работать @ Before / @ Afters. См. Tempus-fugit.googlecode.com/svn/site/documentation/… (прокрутите вниз до тестирования нагрузки / выдержки) для получения дополнительных сведений.
Тоби
0

Я создаю модуль, позволяющий проводить такие тесты. Но не только в повторении. Но чтобы гарантировать, что какой-то фрагмент кода является потокобезопасным.

https://github.com/anderson-marques/concurrent-testing

Зависимость от Maven:

<dependency>
    <groupId>org.lite</groupId>
    <artifactId>concurrent-testing</artifactId>
    <version>1.0.0</version>
</dependency>

Пример использования:

package org.lite.concurrent.testing;

import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import ConcurrentTest;
import ConcurrentTestsRule;

/**
 * Concurrent tests examples
 */
public class ExampleTest {

    /**
     * Create a new TestRule that will be applied to all tests
     */
    @Rule
    public ConcurrentTestsRule ct = ConcurrentTestsRule.silentTests();

    /**
     * Tests using 10 threads and make 20 requests. This means until 10 simultaneous requests.
     */
    @Test
    @ConcurrentTest(requests = 20, threads = 10)
    public void testConcurrentExecutionSuccess(){
        Assert.assertTrue(true);
    }

    /**
     * Tests using 10 threads and make 20 requests. This means until 10 simultaneous requests.
     */
    @Test
    @ConcurrentTest(requests = 200, threads = 10, timeoutMillis = 100)
    public void testConcurrentExecutionSuccessWaitOnly100Millissecond(){
    }

    @Test(expected = RuntimeException.class)
    @ConcurrentTest(requests = 3)
    public void testConcurrentExecutionFail(){
        throw new RuntimeException("Fail");
    }
}

Это проект с открытым исходным кодом. Не стесняйтесь совершенствоваться.

Андерсон Маркес
источник
0

Вы можете запустить тест JUnit из основного метода и повторять его столько раз, сколько вам нужно:

package tests;

import static org.junit.Assert.*;

import org.junit.Test;
import org.junit.runner.Result;

public class RepeatedTest {

    @Test
    public void test() {
        fail("Not yet implemented");
    }

    public static void main(String args[]) {

        boolean runForever = true;

        while (runForever) {
            Result result = org.junit.runner.JUnitCore.runClasses(RepeatedTest.class);

            if (result.getFailureCount() > 0) {
                runForever = false;
               //Do something with the result object

            }
        }

    }

}
silver_mx
источник
0

По сути, это ответ, который Ишай дал выше, переписанный на Котлине:

@RunWith(Parameterized::class)
class MyTest {

    companion object {

        private const val numberOfTests = 200

        @JvmStatic
        @Parameterized.Parameters
        fun data(): Array<Array<Any?>> = Array(numberOfTests) { arrayOfNulls<Any?>(0) }
    }

    @Test
    fun testSomething() { }
}
отметка
источник