Łatwy sposób na powtarzanie tego samego testu junit?

123

Jak mówi tytuł, szukam prostego sposobu na automatyczne uruchamianie testów JUnit 4.x kilka razy z rzędu za pomocą Eclipse.

Przykładem może być uruchomienie tego samego testu 10 razy z rzędu i przedstawienie wyniku.

Mamy już złożony sposób, aby to zrobić, ale szukam prostego sposobu, aby to zrobić, aby mieć pewność, że niestabilny test, który próbowałem naprawić, pozostaje naprawiony.

Idealnym rozwiązaniem byłaby wtyczka / ustawienie / funkcja Eclipse, o której nie wiem.

Stefan Thyberg
źródło
5
Jestem bardzo ciekawy, dlaczego chcesz to zrobić.
Buhb
Przeprowadzam duży test czarnej skrzynki, wprowadziłem małą zmianę i chcę zobaczyć, jak to wpłynęło na stabilność tego wcześniej niestabilnego testu.
Stefan Thyberg,
Tak jest, z wyjątkiem tego, że chcesz, aby działał do niepowodzenia, podczas gdy ja chcę go uruchomić kilka razy, co może wpłynąć na odpowiedzi, które otrzymuję.
Stefan Thyberg,
4
Czy jesteś przeciwko TestNG, ponieważ jeśli nie, możesz po prostu użyć @Test (invocationCount = 10) i to wszystko, co w tym jest.
Robert Massaioli,
1
Nie byłem „przeciw” TestNG, po prostu nie używaliśmy go w tym projekcie.
Stefan Thyberg,

Odpowiedzi:

123

Najłatwiejszym sposobem (ponieważ wymagana jest najmniejsza ilość nowego kodu) jest uruchomienie testu jako testu sparametryzowanego (opatrzenie adnotacją @RunWith(Parameterized.class)i dodanie metody zapewniającej 10 pustych parametrów). W ten sposób framework uruchomi test 10 razy.

Ten test musiałby być jedynym testem w klasie lub lepiej umieścić wszystkie metody testowe w klasie 10 razy.

Oto przykład:

@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");
    }
}

Dzięki temu można to zrobić nawet za pomocą konstruktora bez parametrów, ale nie jestem pewien, czy autorzy frameworka to zamierzyli, czy też w przyszłości się to zepsuje.

Jeśli wdrażasz własnego biegacza, możesz poprosić biegacza o wykonanie testu 10 razy. Jeśli korzystasz z zewnętrznego narzędzia uruchamiającego , to w wersji 4.7 możesz użyć nowej @Ruleadnotacji i zaimplementować MethodRuleinterfejs tak, aby pobierał instrukcję i wykonywał ją 10 razy w pętli for. Obecną wadą tego podejścia jest to @Beforei @Afteruruchamianie tylko raz. Prawdopodobnie zmieni się to w następnej wersji JUnit ( @Beforeuruchomi się po @Rule), ale niezależnie od tego, będziesz działał na tej samej instancji obiektu (coś, co nie jest prawdą w przypadku Parameterizedrunnera). Zakłada się, że niezależnie od biegacza, z którym prowadzisz zajęcia, poprawnie rozpoznaje @Ruleadnotacje. Dzieje się tak tylko w przypadku delegowania do biegaczy JUnit.

Jeśli biegasz z niestandardowym biegaczem, który nie rozpoznaje @Ruleadnotacji, naprawdę utkniesz z koniecznością napisania własnego biegacza, który deleguje odpowiednio tego biegacza i uruchamia go 10 razy.

Zauważ, że istnieją inne sposoby, aby potencjalnie rozwiązać ten problem (np. Biegacz teorii), ale wszystkie wymagają biegacza. Niestety JUnit nie obsługuje obecnie warstw runnerów. To biegacz, który wiąże innych biegaczy.

Yishai
źródło
2
Niestety biegam już @RunWith z innym biegaczem, ale w innym przypadku byłoby to idealne rozwiązanie.
Stefan Thyberg
Tak, jest to rozwiązanie, które chciałbym mieć i które będzie najlepsze dla większości ludzi, więc zgodzę się na odpowiedź.
Stefan Thyberg
Aby uzyskać alternatywne i prawdopodobnie mniej hakerskie rozwiązanie, zobacz: stackoverflow.com/a/21349010/281545
Mr_and_Mrs_D
Niezłe rozwiązanie! Wystąpił wyjątek informujący mnie, że metoda danych powinna zwracać Iterowalne tablice. Poprawiłem to odpowiednio: @ Parameterized.Parameters public static Iterable <Object []> data () {return Arrays.asList (new Object [20] [0]); }
nadre
1
Czy mógłbyś podać link do tej odpowiedzi dla JUnit 5? Opisuje żądaną funkcję, która została dodana w JUnit 5
Marcono1234
102

Dzięki IntelliJ możesz to zrobić z konfiguracji testowej. Po otwarciu tego okna możesz uruchomić test dowolną liczbę razy.

wprowadź opis obrazu tutaj

po uruchomieniu testu intellij wykona wszystkie wybrane przez Ciebie testy tyle razy, ile podałeś.

Przykład uruchomienia 624 testów 10 razy: wprowadź opis obrazu tutaj

smac89
źródło
4
To doskonale, teraz, jeśli możesz wskazać sposób na zaćmienie, aby to zrobić, to odpowiedziałoby na pytanie OP do
rzeczy
Poleganie na konkretnym narzędziu obsługującym rzeczywistą logikę lub wymagania jest anty-wzorcem.
Mickael
1
@Mickael Powtarzanie testu N razy zwykle nie jest wymagane podczas testowania. W rzeczywistości testy powinny być deterministyczne, aby bez względu na to, ile razy zostały powtórzone, zawsze dawały ten sam wynik. Czy możesz wyjaśnić anty-wzór, o którym mówisz?
smac89
Jeśli powtórzenie testu było przydatne dla jednego programisty, prawdopodobnie będzie przydatne dla innych. Jeśli więc testowe środowisko uruchomieniowe i kod mogą obsługiwać logikę umożliwiającą powtarzanie, powinno to być preferowane, ponieważ pozwala na faktoryzację wysiłku i rozwiązania oraz pozwala współautorom na użycie tego narzędzia z tym samym wynikiem. Umieszczenie logiki wielokrotnego użytku w obszarze IDE / dewelopera, gdy można ją umieścić w kodzie, jest rodzajem braku faktoryzacji.
Mickael,
68

Zauważyłem, że powtarzająca się adnotacja Springa jest przydatna w takich sytuacjach:

@Repeat(value = 10)

Najnowszy (Spring Framework 4.3.11.RELEASE API) dokument:

Laura
źródło
46
Zmiana ram testowych nie jest tym, co nazwałbym łatwym sposobem na zrobienie tego.
Stefan Thyberg,
3
Nie musisz zmieniać struktury testowej - działa dobrze z JUnit. Główną wadą jest to, że JUnit nadal postrzega to jako pojedynczy test. Więc gdy pęknie po raz pierwszy, wykonanie zatrzyma się. Jeśli jednak nie używasz jeszcze Springa, to prawdopodobnie nie jest to droga, którą chcesz ...
tveon
Wydaje się, że nie działa dla mnie (wiosna 4.3.6 przez Spring Boot 1.5.1)
David Tonhofer
Nie działa dla mnie z butem sprężynowym 2.1.6 i junit 5
jo-
Doskonale współpracuje z Spring Boot 2. Nie zapomnij dodać @RunWith (SpringRunner :: class), zgodnie z linkiem „testy jednostkowe wiosną” na plakacie!
Agoston Horvath,
33

Zainspirowany następującymi zasobami:

Przykład

Utwórz i użyj @Repeatadnotacji w następujący sposób:

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

Korzystanie z tego rozwiązania w programie @RunWith(PowerMockRunner.class)wymaga aktualizacji do Powermock 1.6.5 (która zawiera poprawkę ).

R. Oosterholt
źródło
Tak. Jak wykonujesz testy?
R. Oosterholt
Sam nie używam zaćmienia. Może nie używasz biegacza testowego Junut 4? (patrz dokument „Dostosowywanie konfiguracji testu” )
R. Oosterholt
29

Dzięki JUnit 5 udało mi się rozwiązać ten problem za pomocą adnotacji @RepeatedTest :

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

Pamiętaj, że @Testadnotacji nie należy używać razem z @RepeatedTest.

César Alberca
źródło
Brzmi bardzo obiecująco, ale nie ma jeszcze wersji wydania.
9ilsdx 9rvj 0lo
1
JUnit 5 trafił do GA kilka tygodni temu.
mkobit
11

Coś nie tak z:

@Test
void itWorks() {
    // stuff
}

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

W przeciwieństwie do przypadku, gdy testujesz każdą z tablicy wartości, nie obchodzi cię, który przebieg się nie powiódł.

Nie musisz robić w konfiguracji ani adnotacji tego, co możesz zrobić w kodzie.

soru
źródło
2
Chciałbym uruchomić kilka testów jako normalne testy jednostkowe i uzyskać śledzenie i status każdego z nich.
Stefan Thyberg
24
W tym przypadku „@Before” s i „@After” s nie zostaną uruchomione
Bogdan
3
To wraz z ręcznym wywołaniem @Beforemetody z adnotacjami przed itWorks() rozwiązaniem mojego problemu.
João Neves
Czy znasz koncepcję DRY? en.wikipedia.org/wiki/Don%27t_repeat_yourself Zalecam dokonanie konfiguracji zamiast kopiowania i wklejania pętli wszędzie.
Kikiwa
Kolejka edycji tej odpowiedzi jest pełna; dlatego umieszczę to w komentarzu: w przypadku JUnit4 testy muszą być publiczne.
Richard Jessop
7

U mnie to działa dużo łatwiej.

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
źródło
Niesamowite, ponieważ nie używa innego frameworka i faktycznie działa z JUnit 3 (kluczowym dla Androida)
Vladimir Ivanov
1
Implementację z JUnit4 można wykonać za pomocą Runnera: 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); } } }Chociaż przynajmniej we wtyczce Eclipse JUnit otrzymujesz wyniki takie jak: „Test 10/1 zdany”
Peter Wippermann
7

W bibliotece tempus-fugit znajduje się przerywana adnotacja, która współpracuje z JUnit 4.7, @Ruleaby powtórzyć test kilka razy lub z @RunWith.

Na przykład,

@RunWith(IntermittentTestRunner.class)
public class IntermittentTestRunnerTest {

   private static int testCounter = 0;

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

Po uruchomieniu testu (z IntermittentTestRunner w @RunWith), testCounterbędzie równe 99.

Toby
źródło
Tak, to ten sam problem, już używam innego biegacza, więc nie mogę użyć tego, chociaż dobry pomysł.
Stefan Thyberg,
Tak, mam ten sam problem z RunWith ... po pewnym czasie poprawiłem tempus-fugit, aby go trochę obejść, możesz użyć @Rule zamiast runner, jeśli chcesz biegać wielokrotnie. Oznaczasz to @Repeating zamiast przerywanym. Jednak wersja reguły nie będzie działać @ Before / @ After. Więcej szczegółów znajdziesz na tempus-fugit.googlecode.com/svn/site/documentation/… (przewiń w dół, aby załadować / zanurzyć testy).
Toby
0

Buduję moduł umożliwiający wykonanie tego typu testów. Ale skupia się nie tylko na powtórzeniach. Ale gwarantujemy, że jakiś fragment kodu jest bezpieczny dla wątków.

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

Zależność Mavena:

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

Przykład użycia:

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");
    }
}

To jest projekt open source. Zapraszam do ulepszania.

Anderson Marques
źródło
0

Możesz uruchomić test JUnit z głównej metody i powtórzyć go tyle razy, ile potrzebujesz:

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
źródło
0

Jest to w istocie odpowiedź, którą Yishai podał powyżej, przepisaną w Kotlinie:

@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() { }
}
znak
źródło