Как я могу воспользоваться преимуществами параллелизма CircleCI в своем проекте Java/Maven/Surefire?

У меня есть проект Java, который использует Maven и maven-surefire-plugin для запуска тестов JUnit 4. . Я работаю с CircleCI. Как включить параллелизм, чтобы мой набор тестов работал быстрее?

Я хочу использовать параллелизм CircleCI, а не Разветвление Surefire и параллельное выполнение.


person JBCP    schedule 24.09.2014    source источник


Ответы (1)


Плагин maven-surefire-plugin поддерживает его не поддерживает параллелизм, по крайней мере, не поддерживает изолированный способ CircleCI (отдельные узлы для каждого выполнения теста).

Однако вы можете вручную включить параллелизм в стиле CircleCI двумя способами:

  1. Используйте сценарий оболочки, чтобы выбрать тесты для запуска на каждом узле, а затем используйте параметр -Dtest.
  2. Пользовательский JUnit 4 TestRule

Сценарий оболочки

Создайте каталог bin в своем проекте, если у вас его еще нет.

В bin создайте сценарий оболочки в своем проекте с именем test.sh со следующим содержимым

#!/bin/bash

NODE_TOTAL=${CIRCLE_NODE_TOTAL:-1}
NODE_INDEX=${CIRCLE_NODE_INDEX:-0}

i=0
tests=()
for file in $(find ./src/test/java -name "*Test.java" | sort)
do
  if [ $(($i % ${NODE_TOTAL})) -eq ${NODE_INDEX} ]
  then
    test=`basename $file | sed -e "s/.java//"`
    tests+="${test},"
  fi
  ((i++))
done

mvn -Dtest=${tests} test

Этот скрипт будет искать в вашем каталоге src/test/java все файлы, оканчивающиеся на Test.java, и добавлять их в параметр -Dtest в виде списка, разделенного запятыми, а затем вызывать maven.

Чтобы включить новый тестовый сценарий, поместите в файл circle.yml следующее:

test:
  override:
    - ./bin/test.sh:
        parallel: true

Что следует отметить:

  1. Вам может потребоваться настроить этот скрипт, если ваши имена файлов не соответствуют этому соглашению об именовании, ваши файлы расположены в другом месте или вам нужно запустить другую фазу жизненного цикла.
  2. Если у вас очень много тестов, вы можете обнаружить, что ваш параметр -Dtest превышает максимальную длину командной строки Linux.

Junit4 TestRule

Вы можете использовать настраиваемое TestRule, чтобы сделать что-то похожее на приведенное выше в коде Java. Преимущество этого заключается в меньшей индивидуальной конфигурации CircleCI, но налагает некоторые предположения о CircleCI в вашей среде Java.

import lombok.extern.slf4j.Slf4j;

import org.apache.commons.lang3.StringUtils;
import org.junit.Assume;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;

@Slf4j
final class CircleCiParallelRule implements TestRule {
    @Override
    public Statement apply(Statement statement, Description description) {

        boolean runTest = true;

        final String tName = description.getClassName() + "#" + description.getMethodName();

        final String numNodes = System.getenv("CIRCLE_NODE_TOTAL");
        final String curNode = System.getenv("CIRCLE_NODE_INDEX");

        if (StringUtils.isBlank(numNodes) || StringUtils.isBlank(curNode)) {
            log.trace("Running locally, so skipping");
        } else {
            final int hashCode = Math.abs(tName.hashCode());

            int nodeToRunOn = hashCode % Integer.parseInt(numNodes);
            final int curNodeInt = Integer.parseInt(curNode);

            runTest = nodeToRunOn == curNodeInt;

            log.trace("currentNode: " + curNodeInt + ", targetNode: " + nodeToRunOn + ", runTest: " + runTest);

            if (!runTest) {
                return new Statement() {
                    @Override
                    public void evaluate() throws Throwable {
                        Assume.assumeTrue("Skipping test, currentNode: " + curNode + ", targetNode: " + nodeToRunOn, false);
                    }
                };
            }
        }

        return statement;
    }
}

(Обратите внимание, что я использую Project Lombok (создание журнала) и Apache Commons-Lang (для StringUtils) в приведенном выше коде, но при необходимости их можно легко удалить.

Чтобы включить это, в своем тестовом базовом классе вы можете сделать это, чтобы сбалансировать тест за тестом:

// This will load-balance across multiple CircleCI nodes
@Rule public CircleCiParallelRule className = new CircleCiParallelRule();

Или, если вы хотите сбалансировать класс за классом, вы можете сделать это:

// This will load-balance across multiple CircleCI nodes
@ClassRule public CircleCiParallelRule className = new CircleCiParallelRule();
person JBCP    schedule 24.09.2014