Uruchamiasz równolegle testy junit w kompilacji Mavena?

110

Używam JUnit 4.4 i Maven i mam wiele długotrwałych testów integracyjnych.

Jeśli chodzi o zrównoleglenie zestawów testów, istnieje kilka rozwiązań, które pozwalają mi równolegle uruchamiać każdą metodę testową w jednej klasie testowej. Ale to wszystko wymaga, abym zmienił testy w taki czy inny sposób.

Naprawdę myślę, że byłoby dużo czystszym rozwiązaniem równoległe uruchamianie X różnych klas testowych w wątkach X. Mam setki testów, więc nie obchodzi mnie wątkowanie poszczególnych klas testowych.

Czy jest na to sposób?

krosenvold
źródło

Odpowiedzi:

75

Użyj wtyczki maven:

<build>
    <plugins>
    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-surefire-plugin</artifactId>
        <version>2.7.1</version>
        <configuration>
            <parallel>classes</parallel>
            <threadCount>5</threadCount>
        </configuration>
    </plugin>
    </plugins>
</build>
Oleksandr
źródło
12
<parallel> jest faktycznie obsługiwany przez surefire, jeśli używasz Junit 4.7 lub nowszego.
Pewny
42

Od junit 4.7 można teraz uruchamiać testy równolegle bez używania TestNG. Właściwie było to możliwe od wersji 4.6, ale w 4.7 wprowadzono szereg poprawek, które sprawią, że będzie to opłacalna opcja. Możesz także przeprowadzić równoległe testy ze sprężyną, o których możesz przeczytać tutaj

krosenvold
źródło
1
Strona, do której prowadzi łącze, zawiera informację „w przypadku większości rozwiązań dwurdzeniowych uruchamianie z równoległymi wątkami nigdy nie jest szybsze niż uruchamianie bez wątków”. Jest to ciągle aktualne?
Raedwald
2
Pomyślałbym, że jeśli twoje testy wykonają jakiekolwiek IO, nadal będą na tym korzystać. Na przykład, jeśli testy jednostkowe są bardziej podobne do testów integracyjnych i trafiają do bazy danych, równoległe uruchamianie powinno je przyspieszyć.
Dave
@Raedwald Nie oczekuj zbyt wiele w przypadku krótkich testów jednostkowych niezwiązanych z io, to próbuję powiedzieć. Nowsze wersje surefire są również lepsze / wydajniejsze niż 2.5 opisane w poście, więc możesz uzyskać nieco lepsze wyniki.
krosenvold
3
Stwierdzasz, że jest to możliwe, ale czy możesz dołączyć link do wyjaśnienia, jak to zrobić? Twój drugi link jest dla „z wiosny”, który nie jestem zainteresowany.
Cory Kendall
Link @krosenvold? Próbuję znaleźć wbudowane rozwiązanie.
Ilan Biala
10

Zainspirowany eksperymentalnym programem ParallelComputer firmy JUnit, stworzyłem własne programy do biegania ParallelSuite i ParallelParameterized . Korzystając z tych programów uruchamiających, można łatwo zrównoleglać zestawy testów i testy parametryzowane.

ParallelSuite.java

public class ParallelSuite extends Suite {

    public ParallelSuite(Class<?> klass, RunnerBuilder builder) throws InitializationError {

        super(klass, builder);

        setScheduler(new RunnerScheduler() {

            private final ExecutorService service = Executors.newFixedThreadPool(4);

            public void schedule(Runnable childStatement) {
                service.submit(childStatement);
            }

            public void finished() {
                try {
                    service.shutdown();
                    service.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
                } catch (InterruptedException e) {
                    e.printStackTrace(System.err);
                }
            }
        });
    }
}

ParallelParameterized.java

public class ParallelParameterized extends Parameterized {

    public ParallelParameterized(Class<?> arg0) throws Throwable {

        super(arg0);

        setScheduler(new RunnerScheduler() {

            private final ExecutorService service = Executors.newFixedThreadPool(8);

            public void schedule(Runnable childStatement) {
                service.submit(childStatement);
            }

            public void finished() {
                try {
                    service.shutdown();
                    service.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
                } catch (InterruptedException e) {
                    e.printStackTrace(System.err);
                }
            }
        });
    }
}

Użycie jest proste. Po prostu zmień wartość adnotacji @RunWith na jedną z tych klas Parallel * .

@RunWith(ParallelSuite.class)
@SuiteClasses({ATest.class, BTest.class, CTest.class})
public class ABCSuite {}
Mustafa Ulu
źródło
5

tempus-fugit oferuje coś podobnego, sprawdź szczegóły w dokumentacji. Opiera się na JUnit 4.7 i po prostu zaznacz swój test@RunWith(ConcurrentTestRunner) .

Twoje zdrowie

Toby
źródło
3

Możesz sprawdzić bibliotekę open source - Test Load Balancer . Robi dokładnie to, o co prosisz - równolegle uruchamiaj różne klasy testowe. To integruje się na poziomie ant-junit, dzięki czemu nie musisz w żaden sposób zmieniać swoich testów. Jestem jednym z autorów biblioteki.

Pomyśl także o tym, aby nie uruchamiać ich w wątkach, ponieważ możesz potrzebować piaskownicy na poziomie procesu. Na przykład, jeśli trafiasz do bazy danych w testach integracji, nie chcesz, aby jeden test zakończył się niepowodzeniem, ponieważ inny test dodał dane w innym wątku. W większości przypadków testy nie są pisane z myślą o tym.

Wreszcie, jak rozwiązałeś dotychczas ten problem?

Pavan
źródło
2

TestNG może to zrobić (to był mój pierwszy odruch - wtedy zobaczyłem, że masz już wiele przypadków testowych).

W przypadku JUnit spójrz na połączenie równoległe .

filant
źródło
3
Niestety nie jest to odpowiedź na zadane przeze mnie pytanie. parallel-junit działa tylko w jednej klasie testowej. TestNG działa również tylko w ramach jednej klasy, a moje testy nie są testami TestNG.
krosenvold
@PlatinumAzure: Zaktualizowałem łącze. Nie wiem, jak ten projekt jest utrzymywany. Ostatnio zadano inne pytanie o dystrybucję wykonywania testów junit na kilku maszynach .
filant
2

Testy można uruchamiać równolegle przy użyciu komputera ParallelComputer dostarczonego przez firmę Junit. Oto mały fragment na początek.

Class[] cls = { TestCase1.class, TestCase2.class };
Result result = JUnitCore.runClasses(ParallelComputer.classes(), cls);
List<Failure> failures = result.getFailures();

Pomoże to, gdy musisz uruchomić testy z kodu, ponieważ nie ma on żadnych zależności od Maven ani innych narzędzi do zarządzania kompilacją.

Zwróć uwagę, że spowoduje to równoległe uruchomienie wszystkich przypadków testowych, jeśli masz jakiekolwiek zależności między różnymi przypadkami testowymi, może to spowodować fałszywe alarmy. I tak NIE POWINIENEŚ poddawać się niezależnym testom.

Ashwin Sadeep
źródło
0

Inny wybór: Punner, nowy równoległy junit runner i wtyczka maven. Nie musisz zmieniać swojego kodu, skopiuj go do swojego pom.xml:

<!-- Disable default surefire based testing -->
<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-surefire-plugin</artifactId>
  <version>2.20</version>
  <configuration>
    <skip>true</skip>
  </configuration>
</plugin>

<plugin>
  <groupId>com.github.marks-yag</groupId>
  <artifactId>punner-maven-plugin</artifactId>
  <version>${version}</version>
  <configuration>
  </configuration>
  <executions>
    <execution>
      <id>test</id>
      <phase>test</phase>
      <goals>
        <goal>test</goal>
      </goals>
    </execution>
  </executions>
</plugin>

Punner może równolegle uruchamiać metody testowe, może utrzymywać wyniki testowe oddzielnie i czyste.

Punner zredukuje wyjście konsoli mvn, na przykład:

[INFO] --- punner-maven-plugin:0.9.13:test (test) @ ipc ---
[INFO] Punner report directory: /Users/guile/workspace/ipc/target/punner-reports
[INFO]
[INFO] com.github.yag.ipc.IPCTest.testConnectionHandler.............. PASSED
[INFO] com.github.yag.ipc.IPCTest.testSequence....................... PASSED
[INFO] com.github.yag.ipc.IPCTest.testPartialContent................. PASSED
[INFO] com.github.yag.ipc.IPCTest.testResponseContent................ PASSED
[INFO] com.github.yag.ipc.IPCTest.testPingPong....................... PASSED
[INFO] com.github.yag.ipc.IPCTest.testServerClose.................... PASSED
[INFO] com.github.yag.ipc.IPCTest.testServerSideHeartbeatTimeout..... PASSED
[INFO] com.github.yag.ipc.IPCTest.testClientSideHeartbeatTimeout..... PASSED
[INFO] com.github.yag.ipc.IPCTest.testClientSideHeartbeat............ PASSED
[INFO] com.github.yag.ipc.IPCTest.testClientReconnect................ PASSED
[INFO]
[INFO] Tests run: 10, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 10.952 sec, Time saved: 25.919 sec.

Punner produkuje z pewnością kompatybilne dane wyjściowe, możesz również uzyskać nieprzetworzone dane dziennika i raport w formacie przeceny z katalogu raportów:

  ipc git:(develop) ll target/punner-reports
total 104
-rw-r--r--   1 guile  staff    11K Oct 15 23:07 TEST-com.github.yag.ipc.IPCTest.xml
-rw-r--r--   1 guile  staff   298B Oct 15 23:07 com.github.yag.ipc.IPCTest.txt
drwxr-xr-x  12 guile  staff   384B Oct  8 00:50 logs
-rw-r--r--   1 guile  staff    33K Oct 15 23:07 report.md

Punner to mój osobisty projekt, napisałem Punner, aby przyspieszyć fazę testów jednostkowych innych projektów, takich jak framework IPC, precyzyjne blokowanie, obsługa dzienników, silnik rozproszonego przepływu pracy, itp. To zaoszczędziło mi dużo czasu.

Punner nie obsługuje jeszcze niektórych zaawansowanych funkcji. Bardzo się cieszę, gdybyś mógł spróbować i przekazać mi swoją opinię.

Guilin Sun
źródło
-3

Możesz zmienić swój test na TestNg w ciągu minuty (wystarczy zmienić import), TestNG jest najlepszy w testowaniu równoległym.


źródło
-3

Możesz wypróbować Gridgain, który umożliwia uruchamianie dystrybucji testów w siatce obliczeniowej.

Jan Kronquist
źródło
1
Wypróbowałem rozwiązanie GridGain i miałem dwa poważne problemy. Po pierwsze, musisz powiedzieć GridGain, aby wykluczył ze ścieżki klas twojego zadania grid wszystko, czego używa GridGain, np. Spring i wiele rzeczy z Apache Commons. Po drugie, sieciowe ładowanie klas, choć genialny pomysł, nie działa dla bibliotek, które chcą przeszukiwać ścieżkę klas, np. Wiosna
Graham Lea