我有一个使用 Maven 和maven-surefire-plugin
运行 JUnit 4 测试的 Java 项目。我正在使用CircleCI 构建。如何启用并行性以使我的测试套件运行得更快?
我想使用 CircleCI 并行性,而不是Surefire fork 和并行执行选项。
我有一个使用 Maven 和maven-surefire-plugin
运行 JUnit 4 测试的 Java 项目。我正在使用CircleCI 构建。如何启用并行性以使我的测试套件运行得更快?
我想使用 CircleCI 并行性,而不是Surefire fork 和并行执行选项。
maven-surefire-plugin 支持其不支持并行性,至少不以孤立的方式 CircleCI 支持(每个测试执行的单独节点)。
但是,您可以使用两种方法手动启用 CircleCI 样式的并行性:
-Dtest
参数。TestRule
bin
如果您还没有目录,请在您的项目中创建一个目录。
在bin
您的项目中创建一个名为 的 shell 脚本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
注意事项:
-Dtest
参数超过了 Linux 命令行的最大长度。您可以使用自定义 TestRule 在 Java 代码中执行与上述类似的操作。这具有较少的 CircleCI 自定义配置的优点,但在您的 Java 框架上强加了有关 CircleCI 的一些假设。
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();