【发布时间】:2020-07-24 17:02:15
【问题描述】:
相关:Ant Junit tests are running much slower via ant than via IDE - what to look at?
我和另一个问题有同样的问题,但我已经缩小了很多范围。
我有一套大型单元测试(即 800 个测试用例),应该由 Atlassian Bamboo CI 运行。我已经完全重写了我的构建脚本。
在 IntelliJ IDEA 的 test 文件夹中运行所有测试时,一切都需要 7 分钟的合理时间,包括基于 Spring 上下文的测试,需要填充有表的新 H2 实例。
在 Ant 中运行时,无论是在本地还是在 CI 上,构建都需要 疯狂 1 小时的时间来测试。让 8 个线程值班将时间减少到 20 分钟(最慢的工作)。
我曾怀疑最重的测试与 Spring 和内存数据库有关。我今天调查了很多,发现了一些性能很差的坏测试,但我在这里是因为我无法理解原因。
大多数表现糟糕的测试完全在内存中,几乎没有系统输出。虽然有相对“大量”的断言操作、迭代等,但相同的测试在 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 写入任何内容,在内存中运行了 4 秒。
同样,很多东西都可以简化或优化,但 4s 与 346s 对 IMO 来说太疯狂了
- BeanUtilsTests
我有一个对属性和反射进行操作的 BeanUtils 实用程序。这个非常单一的测试,包括 AES 加密操作,需要 25 秒才能在 Ant 中运行
@Test
public void testMarshal_Password()
{
String passwordPlaintext = RandomStringUtils.randomAlphanumeric(25);
assertThat(BeanUtils.marshal(passwordPlaintext, PASSWORD), is(not(equalTo(passwordPlaintext))));
}
没有迭代!!!在内部,它是一个重定向到加密实用程序的 switch 语句。密钥在代码中是硬编码的,无需生成随机安全密钥。 Cipher.Init 然后加密随机字符串。
在 IDE 中需要 1s,初始化测试运行时可能比执行测试要多得多。
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"只是为了确保类路径不受 Ant 相关类的感染 -
showoutput="false"减少 IO。特别是,Bamboo 远程代理将stdout传输给主服务器,从而降低了性能 -
forkmode我也尝试过具有并行性的 perTest 模式。现在我恢复为单线程模式,因为 Atlassian Bamboo 在并行代理上运行 -
logfailedtests="true"失败很重要,并非所有测试都失败
问题
有人帮忙吗?我需要我的构建在几乎没有体面的时间内运行
更新
更新 1:缩小范围
我现在正尝试适当地使用参数在本地 Ant 上运行单个测试类。我发现 SortedLinkedListTests 运行缓慢,所以我现在尝试单独运行它。这仍然需要很多时间。
我通过将另一个测试方法的迭代次数减少了一个数量级,设法减少了 BeanUtilsTests 的执行时间。然后,即使是密码测试也能像魅力一样运行。
更新 2:套件帮助
我试图回忆自从我从头开始重写构建并复制以来发生了什么变化。
我之前的构建有一个类似的run-junit Ant 任务,它只调用了一个@Suite-annotated 类文件,它引用了所有手动运行的测试。这是我重写构建的原因之一。我检查了 Bamboo 过去的测试日志。 SortedLinkedListTests 与之前的构建在几秒钟内执行
这是老跑者。我看不出有什么东西会降低性能如此艰难和糟糕
<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>
【问题讨论】:
标签: java unit-testing junit ant