Параллельное выполнение тестов junit в сборке Maven?

110

Я использую JUnit 4.4 и Maven, и у меня есть большое количество длительных интеграционных тестов.

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

Я действительно думаю, что было бы намного более чистым решением запустить X разных тестовых классов в X потоках параллельно. У меня есть сотни тестов, поэтому меня не волнует распараллеливание отдельных тестовых классов.

Есть какой-либо способ сделать это?

Кросенвольд
источник

Ответы:

75

Используйте плагин 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>
Александр
источник
12
<parallel> действительно поддерживается surefire, если вы используете Junit 4.7 или новее. уверен гид
jontej
42

Начиная с junit 4.7, теперь можно запускать тесты параллельно без использования TestNG. На самом деле это стало возможным с версии 4.6, но в 4.7 внесен ряд исправлений, которые сделают это жизнеспособным вариантом. Вы также можете запускать параллельные тесты с помощью spring, о которых вы можете прочитать здесь

Кросенвольд
источник
1
На связанной странице сказано: «для большинства двухъядерных решений работа с параллельными потоками в настоящее время никогда не бывает быстрее, чем работа без потоков». Так ли это до сих пор?
Raedwald,
2
Я бы подумал, что если ваши тесты будут выполнять операции ввода-вывода, они все равно выиграют. Например, если ваши модульные тесты больше похожи на интеграционные тесты и попадают в базу данных, параллельный запуск должен ускорить их.
Дэйв
@Raedwald Не ожидайте слишком многого от коротких модульных тестов без привязки к io - вот что я пытаюсь сказать. Более новые версии surefire также лучше / эффективнее, чем 2.5, описанные в сообщении, поэтому вы можете получить немного лучшие результаты.
krosenvold 04
3
Вы заявляете, что это возможно, но можете ли вы включить ссылку на объяснение того, как? Ваша вторая ссылка предназначена для «с пружиной», что меня не интересует.
Кори Кендалл,
@krosenvold ссылка? Я изо всех сил пытаюсь найти встроенное решение.
Илан Бяла
10

Вдохновленный экспериментальным средством запуска ParallelComputer от JUnit, я создал свои собственные средства запуска ParallelSuite и ParallelParameterized . Используя эти бегуны, можно легко распараллелить наборы тестов и параметризованные тесты.

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

Использование простое. Просто измените значение аннотации @RunWith на один из этих классов Parallel * .

@RunWith(ParallelSuite.class)
@SuiteClasses({ATest.class, BTest.class, CTest.class})
public class ABCSuite {}
Мустафа Улу
источник
5

tempus-fugit предлагает нечто подобное, подробности смотрите в документации. Он основан на JUnit 4.7, и вы просто отмечаете свой тест @RunWith(ConcurrentTestRunner).

ура

Тоби
источник
3

Вы можете проверить библиотеку с открытым исходным кодом - Test Load Balancer . Он делает именно то, что вы просите, - параллельно запускает разные тестовые классы. Это интегрируется на уровне ant-junit, так что вам ни в коем случае не нужно менять свои тесты. Я один из авторов библиотеки.

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

Наконец, как решили эту проблему до сих пор?

павана
источник
2

TestNG может это сделать (это был мой первый рефлекс - потом я увидел, что у вас уже есть много тестов).

Для JUnit посмотрите parallel-junit .

филант
источник
3
К сожалению, это не ответ на вопрос, который я задаю. parallel-junit работает только в одном тестовом классе. TestNG также работает только в одном классе, и мои тесты не являются тестами TestNG.
krosenvold 08
@PlatinumAzure: Я обновил ссылку. Я не знаю, как поддерживается этот проект. Недавно был задан еще один вопрос о распределении выполнения тестов junit на нескольких машинах .
филант
2

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

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

Это поможет, когда вам нужно запускать тесты из кода, поскольку он не зависит от Maven или других инструментов управления сборкой.

Обратите внимание, что при этом все тестовые примеры будут запускаться параллельно, если у вас есть какие-либо зависимости между разными тестами, это может привести к ложным срабатываниям. В любом случае вам НЕ СЛЕДУЕТ проводить взаимозависимые тесты.

Эшвин Садип
источник
0

Другой вариант: Punner, новый параллельный модуль runner и maven. Вам не нужно менять свой код, скопируйте его в свой 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 может запускать методы тестирования параллельно, может сохранять результаты тестов отдельно и в чистоте.

Punner уменьшит выходные данные консоли mvn, например:

[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 дает надежные совместимые результаты, вы также можете получить необработанные данные журнала и отчет в формате уценки из каталога отчетов:

  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 - это мой личный проект, я написал Punner, чтобы ускорить этап модульного тестирования некоторых других проектов, таких как инфраструктура IPC, детализированная блокировка, служба журналов, механизм распределенного рабочего процесса и т. Д. Это сэкономило мне много времени на ожидание.

Punner пока не поддерживает некоторые дополнительные функции. Я очень рад, если вы могли бы попробовать и дать мне несколько отзывов.

Гуйлинь Сунь
источник
-3

Вы можете изменить свой тест на TestNg за минуту (вам просто нужно изменить импорт), TestNG - лучший вариант для параллельного тестирования.


источник
-3

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

Ян Кронквист
источник
1
Я попробовал решение GridGain и столкнулся с двумя серьезными проблемами. Во-первых, вы должны указать GridGain исключить из пути к классам вашей грид-задачи все, что GridGain также использует, например Spring и множество других вещей Apache Commons. Во-вторых, загрузка классов по сети, хотя и является блестящей идеей, не работает для библиотек, которые хотят искать путь к классам, например Spring
Грэм Ли