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


121

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

Прикладом може бути те саме тестування 10 разів підряд та звітування про результат.

У нас вже є складний спосіб зробити це, але я шукаю простий спосіб зробити це, щоб я міг бути впевненим, що лускатий тест, який я намагався виправити, залишається виправленим.

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


5
Мені дуже цікаво, чому ти хотів би це зробити.
Бух

Я проводжу великий тест на чорний ящик, внесли невеликі зміни і хочу побачити, як це вплинуло на стабільність цього раніше лускатого тесту.
Стефан Тіберг

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

4
Ви проти TestNG, тому що якщо ні, то ви можете просто використовувати @Test (invocationCount = 10), і це все, що там є.
Роберт Массайолі

1
Я не був "проти" TestNG, ми просто не використовували його в тому проекті.
Стефан Тіберг

Відповіді:


123

Найпростіший (як мінімум необхідний кількість нового коду) для цього - запустити тест як параметризований тест (анотувати з @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 разів.

Зауважте, що існують й інші способи вирішити це (наприклад, теорія бігуна), але всі вони потребують бігуна. На жаль, JUnit наразі не підтримує шари бігунів. Це бігун, який прикував інших бігунів.


2
На жаль, я вже працюю @RunWith з іншим бігуном, але в іншому випадку це було б ідеальним рішенням.
Стефан Тіберг

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

В якості альтернативного і , можливо , менш Hacky Престолу рішення: stackoverflow.com/a/21349010/281545
Mr_and_Mrs_D

Приємне рішення! Я отримав виняток, який сказав мені, що метод даних повинен повертати Iterable of Arrays. Я це виправив відповідно: @ Parameterized.Parameters public static Iterable <Object []> data () {return Arrays.asList (новий Object [20] [0]); }
надр

1
Чи можете ви надати відповідь на цю відповідь для JUnit 5? Він описує потрібну функцію, яка була додана в JUnit 5
Marcono1234,

100

З IntelliJ ви можете це зробити з тестової конфігурації. Після відкриття цього вікна ви зможете запустити тест будь-яку кількість разів.

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

під час запуску тесту intellij виконає всі вибрані вами тести протягом кількості вказаних вами разів.

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


4
Це ідеально, тепер, якщо ви можете вказати на спосіб затемнення, це відповіло б на питання ОП до кінця
17:33

Покладання на конкретний інструмент для розміщення фактичної логіки чи вимог є анти-закономірністю.
Мікаель

1
@Mickael Повторення тесту N разів зазвичай не є обов'язковим тестуванням. Насправді тести повинні бути детермінованими, так що незалежно від того, скільки разів вона повторюється, вона завжди повинна давати однаковий результат. Чи можете ви пояснити анти шаблон, про який ви говорите?
smac89

Якщо повторення тесту було корисним для 1 розробника, воно може бути корисним для інших. Отже, якщо час виконання тесту та код можуть містити логіку, щоб увімкнути повторення, слід віддати перевагу, оскільки це дозволяє розподілити зусилля та рішення та дозволити розробникам використовувати інструмент "бажано" з тим самим результатом. Введення логіки багаторазового використання в область IDE / розробника, коли вона може бути поміщена в код, є своєрідною відсутністю факторизації.
Мікаель

68

Я виявив, що повторна примітка Spring є корисною для таких речей:

@Repeat(value = 10)

Останній (Spring Framework 4.3.11.RELEASE API) документ:


46
Зміна рамок тесту - це не те, що я б назвав простим способом.
Стефан Тіберг

3
Вам не потрібно змінювати тестовий фреймворк - він добре працює з JUnit. Основним недоліком є ​​те, що JUnit все ще сприймає це як єдиний тест. Тож перший раз, коли він зламається, виконання припиняється. Однак якщо ви вже не використовуєте Spring, то, мабуть, це не той шлях, яким ви хочете пройти ...
tveon

Здається, це не працює для мене (Весна 4.3.6 через Spring Boot 1.5.1)
Девід Тонхофер

Не працює для мене з весняним завантаженням 2.1.6 та 5 липня
липня

Відмінно справляється з весняним завантаженням 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
    }
}

Повторіть.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 (що включає патч ).


Так. Як ви виконуєте тести?
Р. Остерхолт

Я сам не використовую затемнення. Можливо, ви не використовуєте тестовий бігун "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
Я хотів би запустити кілька тестів як звичайні одиничні тести і отримати слід та статус для кожного.
Стефан Тіберг

24
У цьому випадку "@Before" s та "@After" не будуть запущені
Богдан

3
Це разом із ручним викликом @Beforeанотованого методу, перш ніж itWorks() вирішити мою проблему.
Жоао Невес

Чи знаєте ви концепцію DRY? en.wikipedia.org/wiki/Don%27t_repeat_yourself Я рекомендую зробити кілька налаштувань, а не копіювати скрізь петельку скрізь.
Кіківа

Черга редагування для цієї відповіді заповнена; тому я викладу це в коментарі: для 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;
    }
}

Дивовижно як не використовує інший фреймворк і насправді працює з JUnit 3 (вирішальне значення для андроїда)
Володимир Іванов

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 є переривчаста примітка, яка працює з 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 ... в міру того, як я налаштував темпус-втікач, щоб трохи обійти його, ви можете використовувати @Rule, а не бігун, коли хочете повторно запускатись. Ви помічаєте це за допомогою "Повторення" замість переривчастих. Версія правила звичайно не запускається @ До / @ Afters. Щоб отримати докладнішу інформацію, перегляньте сторінку tempus-fugit.googlecode.com/svn/site/documentation/… (прокрутіть униз, щоб завантажити / замочити тестування).
Тобі

0

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

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

Залежна залежність:

<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

            }
        }

    }

}

0

Це, по суті, відповідь, яку Yishai надав вище, переписаний у Котліні:

@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() { }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.