某些 JUnit 测试在 Ant 中花费了大量的时间
Certain JUnit test take insane amounts of time in Ant
相关:Ant Junit tests are running much slower via ant than via IDE - what to look at?
我和另一个问题有同样的问题,但我已经缩小了很多范围。
我有一大套单元测试(即 800 个测试用例),应该是 Atlassian Bamboo 运行 CI。我已经完全重写了我的构建脚本。
当 运行在 IntelliJ test
文件夹中 IDEA 中进行所有测试时,一切都需要 7 分钟的合理时间,包括 Spring-context-based需要用表格填充新的 H2 实例的测试。
当 运行在本地和 CI 上使用 Ant 时,构建需要 疯狂 1 小时的时间来测试。让 8 个线程值班将时间减少到 20 分钟(最慢的工作)。
我曾怀疑最重的测试与 Spring 和内存数据库有关。我今天调查了很多,发现了一些表现很糟糕的糟糕测试,但我在这里是因为我不明白原因。
大多数性能糟糕的测试完全在内存中,几乎没有 none 系统输出。虽然有相对“大量”的断言操作、迭代等,但相同的测试在 Ant 上表现糟糕,在 IDE.
上表现出色
选择以下测试:
- SortedLinkedListTests:1.163 秒
我有一个 SortedLinkedList 组件(具有 O(n) 排序插入的链表)及其单元测试。没有日志记录框架,没有 Spring 上下文。没有。
基本上,单元测试运行 10000 次迭代。以下非常单一的测试耗时 346 秒。
@Test
public void testAddWithoutComparator()
{
final SortedLinkedList<Integer> uut = new SortedLinkedList<>();
final Random random = new Random();
for (int i = 0; i < ITERATIONS; i++)
{
uut.add(random.nextInt());
Integer previous = null;
if (i % 50 == 0)
for (Integer candidate : uut)
{
assertNotNull(candidate);
if (previous != null)
assertTrue(previous.compareTo(candidate) <= 0);
previous = candidate;
}
}
}
也许代码有点笨,可以优化很多,但还不错,没那么。在 1 到 10000 次迭代中,它检查列表是否在每一步都排序,而不仅仅是在开始或结束时。考虑 10000 个增量测试。
这个测试的复杂度为 O(n^2),我对此很满意。我很高兴测试,没有向 IO 写入任何内容,运行s 在 4 秒内存中。
同样,很多东西都可以简化或优化,但 4s vs 346s 在我看来太疯狂了
- BeanUtilsTests
我有一个对属性和反射进行操作的 BeanUtils 实用程序。这个非常单一的测试,包括 AES 加密操作,在 Ant
中需要 25 秒才能达到 运行
@Test
public void testMarshal_Password()
{
String passwordPlaintext = RandomStringUtils.randomAlphanumeric(25);
assertThat(BeanUtils.marshal(passwordPlaintext, PASSWORD), is(not(equalTo(passwordPlaintext))));
}
没有迭代!!!在内部,它是一个重定向到加密实用程序的 switch 语句。密钥硬编码在代码中,无需生成随机安全密钥。 Cipher.Init然后加密随机字符串。
在 IDE 中花费 1 秒,初始化测试 运行 的时间可能比执行测试的时间多很多。
Ant 构建
<!-- JUNIT TARGETS -->
<target name="debug-junit">
<property name="junit.include" value="**/*.java" />
<condition property="jarOk">
<resourcecount count="1">
<fileset id="fs" dir="${project.artifactsDirectory}" includes="${artifact.name}-junit.jar"/>
</resourcecount>
</condition>
<fail message="No jar file. Please package the project first" unless="jarOk"/>
<mkdir dir="${junit.destDir}"/>
<junit printsummary="yes" haltonfailure="yes" dir="${basedir}" forkmode="once" logfailedtests="true" maxmemory="1024m" fork="true" showoutput="false">
<classpath refid="project.classpath.test"/>
<jvmarg value="-Djava.compiler=NONE"/>
<jvmarg value="-agentlib:jdwp=transport=dt_socket,address=${junit.debugPort},server=y,suspend=y"/>
<batchtest todir="${junit.destDir}" haltonerror="false" haltonfailure="false" skipnontests="true">
<fileset dir="${junit.srcDir}" includes="${junit.include}"/>
<formatter type="xml" />
</batchtest>
</junit>
</target>
<target name="run-junit">
<property name="junit.include" value="**/*.java" />
<condition property="jarOk">
<resourcecount count="1">
<fileset id="fs" dir="${project.artifactsDirectory}" includes="${artifact.name}-junit.jar"/>
</resourcecount>
</condition>
<fail message="No jar file. Please package the project first" unless="jarOk"/>
<mkdir dir="${junit.destDir}"/>
<junit printsummary="yes" haltonfailure="yes" dir="${basedir}" forkmode="once" logfailedtests="true" maxmemory="1024m" fork="true" showoutput="false">
<classpath refid="project.classpath.test"/>
<jvmarg value="-Djava.compiler=NONE"/>
<jvmarg value="-ea"/>
<batchtest todir="${junit.destDir}" haltonerror="false" haltonfailure="false" skipnontests="true">
<fileset dir="${junit.srcDir}" includes="${junit.include}"/>
<formatter type="xml" />
</batchtest>
</junit>
</target>
解释:
当我想将调试器附加到调试测试时,junit.debugargs
被覆盖。在这里你没有特定于调试的属性
fork="true"
只是为了确保 classpath 没有 infected 受到 Ant 相关 classes 的影响
showoutput="false"
减少IO。特别是,Bamboo 远程代理将 stdout
传输给 master,降低了性能
forkmode
我也尝试过并行的 perTest 模式。现在我恢复到单线程模式,因为并行代理上的 Atlassian Bamboo 运行s
logfailedtests="true"
失败很重要,并不是所有的测试都会失败
问题
有人帮忙吗?我需要在勉强合适的时间内 运行 构建
更新
Update 1: narrowing the scope
我现在正尝试适当地使用参数来 运行 在我的本地 Ant 上进行单个测试 class。我发现 SortedLinkedListTests 运行 速度很慢,所以我现在正尝试单独 运行 它。还是要花很多时间。
我通过将另一种测试方法的迭代次数减少了一个数量级,成功地减少了 BeanUtilsTests 的执行时间。然后,即使是密码测试 运行 也很有魅力。
Update 2: suite helps
我试图回忆自从我从头开始重写我的构建并复制以来发生了什么变化。
我之前的构建有一个类似的 run-junit
Ant 任务,它只调用一个 @Suite
注释的 class 文件,该文件将所有测试手动引用到 运行。这是我重写构建的原因之一。我已经检查了 Bamboo 过去的测试日志。 SortedLinkedListTests
在几秒钟内执行了之前的构建
这是老 运行ner。我看不到任何会严重降低性能的东西
<target name="run-junit">
<condition property="jarOk">
<resourcecount count="1">
<fileset id="fs" dir="${dir.publish}" includes="${artifact.name}-junit-*.jar"/>
</resourcecount>
</condition>
<fail message="No jar file. Please package the project first" unless="jarOk"/>
<mkdir dir="${dir.junitResult}"/>
<property name="junit.srcDir" value="test"/>
<fail message="Must set -Djunit.testSuite" unless:set="junit.testSuite"/>
<junit printsummary="yes" haltonfailure="yes" dir="${basedir}" fork="true" showoutput="true">
<classpath refid="project.classpath.test"/>
<classpath location="build/compiled-test"/>
<batchtest todir="${dir.junitResult}" haltonerror="false" haltonfailure="false" fork="true">
<fileset dir="${junit.srcDir}" includes="**/${junit.testSuite}.java"/>
<formatter type="xml"/>
</batchtest>
<formatter type="xml"/>
</junit>
</target>
经过数小时的尝试和失败,以下对我有用
移除
<jvmarg value="-Djava.compiler=NONE"/>
我将其标记为社区 Wiki,以便任何人都能够通过解释进行扩展!
相关:Ant Junit tests are running much slower via ant than via IDE - what to look at?
我和另一个问题有同样的问题,但我已经缩小了很多范围。
我有一大套单元测试(即 800 个测试用例),应该是 Atlassian Bamboo 运行 CI。我已经完全重写了我的构建脚本。
当 运行在 IntelliJ test
文件夹中 IDEA 中进行所有测试时,一切都需要 7 分钟的合理时间,包括 Spring-context-based需要用表格填充新的 H2 实例的测试。
当 运行在本地和 CI 上使用 Ant 时,构建需要 疯狂 1 小时的时间来测试。让 8 个线程值班将时间减少到 20 分钟(最慢的工作)。
我曾怀疑最重的测试与 Spring 和内存数据库有关。我今天调查了很多,发现了一些表现很糟糕的糟糕测试,但我在这里是因为我不明白原因。
大多数性能糟糕的测试完全在内存中,几乎没有 none 系统输出。虽然有相对“大量”的断言操作、迭代等,但相同的测试在 Ant 上表现糟糕,在 IDE.
上表现出色选择以下测试:
- SortedLinkedListTests:1.163 秒
我有一个 SortedLinkedList 组件(具有 O(n) 排序插入的链表)及其单元测试。没有日志记录框架,没有 Spring 上下文。没有。
基本上,单元测试运行 10000 次迭代。以下非常单一的测试耗时 346 秒。
@Test
public void testAddWithoutComparator()
{
final SortedLinkedList<Integer> uut = new SortedLinkedList<>();
final Random random = new Random();
for (int i = 0; i < ITERATIONS; i++)
{
uut.add(random.nextInt());
Integer previous = null;
if (i % 50 == 0)
for (Integer candidate : uut)
{
assertNotNull(candidate);
if (previous != null)
assertTrue(previous.compareTo(candidate) <= 0);
previous = candidate;
}
}
}
也许代码有点笨,可以优化很多,但还不错,没那么。在 1 到 10000 次迭代中,它检查列表是否在每一步都排序,而不仅仅是在开始或结束时。考虑 10000 个增量测试。 这个测试的复杂度为 O(n^2),我对此很满意。我很高兴测试,没有向 IO 写入任何内容,运行s 在 4 秒内存中。
同样,很多东西都可以简化或优化,但 4s vs 346s 在我看来太疯狂了
- BeanUtilsTests
我有一个对属性和反射进行操作的 BeanUtils 实用程序。这个非常单一的测试,包括 AES 加密操作,在 Ant
中需要 25 秒才能达到 运行@Test
public void testMarshal_Password()
{
String passwordPlaintext = RandomStringUtils.randomAlphanumeric(25);
assertThat(BeanUtils.marshal(passwordPlaintext, PASSWORD), is(not(equalTo(passwordPlaintext))));
}
没有迭代!!!在内部,它是一个重定向到加密实用程序的 switch 语句。密钥硬编码在代码中,无需生成随机安全密钥。 Cipher.Init然后加密随机字符串。
在 IDE 中花费 1 秒,初始化测试 运行 的时间可能比执行测试的时间多很多。
Ant 构建
<!-- JUNIT TARGETS -->
<target name="debug-junit">
<property name="junit.include" value="**/*.java" />
<condition property="jarOk">
<resourcecount count="1">
<fileset id="fs" dir="${project.artifactsDirectory}" includes="${artifact.name}-junit.jar"/>
</resourcecount>
</condition>
<fail message="No jar file. Please package the project first" unless="jarOk"/>
<mkdir dir="${junit.destDir}"/>
<junit printsummary="yes" haltonfailure="yes" dir="${basedir}" forkmode="once" logfailedtests="true" maxmemory="1024m" fork="true" showoutput="false">
<classpath refid="project.classpath.test"/>
<jvmarg value="-Djava.compiler=NONE"/>
<jvmarg value="-agentlib:jdwp=transport=dt_socket,address=${junit.debugPort},server=y,suspend=y"/>
<batchtest todir="${junit.destDir}" haltonerror="false" haltonfailure="false" skipnontests="true">
<fileset dir="${junit.srcDir}" includes="${junit.include}"/>
<formatter type="xml" />
</batchtest>
</junit>
</target>
<target name="run-junit">
<property name="junit.include" value="**/*.java" />
<condition property="jarOk">
<resourcecount count="1">
<fileset id="fs" dir="${project.artifactsDirectory}" includes="${artifact.name}-junit.jar"/>
</resourcecount>
</condition>
<fail message="No jar file. Please package the project first" unless="jarOk"/>
<mkdir dir="${junit.destDir}"/>
<junit printsummary="yes" haltonfailure="yes" dir="${basedir}" forkmode="once" logfailedtests="true" maxmemory="1024m" fork="true" showoutput="false">
<classpath refid="project.classpath.test"/>
<jvmarg value="-Djava.compiler=NONE"/>
<jvmarg value="-ea"/>
<batchtest todir="${junit.destDir}" haltonerror="false" haltonfailure="false" skipnontests="true">
<fileset dir="${junit.srcDir}" includes="${junit.include}"/>
<formatter type="xml" />
</batchtest>
</junit>
</target>
解释:
-
当我想将调试器附加到调试测试时,
junit.debugargs
被覆盖。在这里你没有特定于调试的属性fork="true"
只是为了确保 classpath 没有infected受到 Ant 相关 classes 的影响
showoutput="false"
减少IO。特别是,Bamboo 远程代理将stdout
传输给 master,降低了性能forkmode
我也尝试过并行的 perTest 模式。现在我恢复到单线程模式,因为并行代理上的 Atlassian Bamboo 运行slogfailedtests="true"
失败很重要,并不是所有的测试都会失败
问题
有人帮忙吗?我需要在勉强合适的时间内 运行 构建
更新
Update 1: narrowing the scope
我现在正尝试适当地使用参数来 运行 在我的本地 Ant 上进行单个测试 class。我发现 SortedLinkedListTests 运行 速度很慢,所以我现在正尝试单独 运行 它。还是要花很多时间。
我通过将另一种测试方法的迭代次数减少了一个数量级,成功地减少了 BeanUtilsTests 的执行时间。然后,即使是密码测试 运行 也很有魅力。
Update 2: suite helps
我试图回忆自从我从头开始重写我的构建并复制以来发生了什么变化。
我之前的构建有一个类似的 run-junit
Ant 任务,它只调用一个 @Suite
注释的 class 文件,该文件将所有测试手动引用到 运行。这是我重写构建的原因之一。我已经检查了 Bamboo 过去的测试日志。 SortedLinkedListTests
在几秒钟内执行了之前的构建
这是老 运行ner。我看不到任何会严重降低性能的东西
<target name="run-junit">
<condition property="jarOk">
<resourcecount count="1">
<fileset id="fs" dir="${dir.publish}" includes="${artifact.name}-junit-*.jar"/>
</resourcecount>
</condition>
<fail message="No jar file. Please package the project first" unless="jarOk"/>
<mkdir dir="${dir.junitResult}"/>
<property name="junit.srcDir" value="test"/>
<fail message="Must set -Djunit.testSuite" unless:set="junit.testSuite"/>
<junit printsummary="yes" haltonfailure="yes" dir="${basedir}" fork="true" showoutput="true">
<classpath refid="project.classpath.test"/>
<classpath location="build/compiled-test"/>
<batchtest todir="${dir.junitResult}" haltonerror="false" haltonfailure="false" fork="true">
<fileset dir="${junit.srcDir}" includes="**/${junit.testSuite}.java"/>
<formatter type="xml"/>
</batchtest>
<formatter type="xml"/>
</junit>
</target>
经过数小时的尝试和失败,以下对我有用
移除
<jvmarg value="-Djava.compiler=NONE"/>
我将其标记为社区 Wiki,以便任何人都能够通过解释进行扩展!