Sán*_*hos 10 java junit4 maven maven-failsafe-plugin
我有一个带有Maven的Java应用程序。Junit用于测试,带有故障安全和surefire插件。我有2000多个集成测试。为了加快测试的运行速度,我使用故障安全jvmfork并行运行测试。我有一些繁重的测试类,它们通常在测试执行结束时运行,这会减慢我的CI验证过程。filesafe runorder:balanced对我来说将是一个不错的选择,但由于jvmfork,我无法使用它。重命名测试类或移动到另一个软件包并按字母顺序运行它不是一种选择。有什么建议可以在验证过程开始时运行慢速测试类吗?
在JUnit 5(从5.8.0-M1版本开始)中,也可以订购测试类。
src/test/resources/ junit-platform.properties :
# ClassOrderer$OrderAnnotation sorts classes based on their @Order annotation
junit.jupiter.testclass.order.default=org.junit.jupiter.api.ClassOrderer$OrderAnnotation
Run Code Online (Sandbox Code Playgroud)
其他 Junit 内置类排序器实现:
org.junit.jupiter.api.ClassOrderer$ClassName
org.junit.jupiter.api.ClassOrderer$DisplayName
org.junit.jupiter.api.ClassOrderer$Random
Run Code Online (Sandbox Code Playgroud)
有关设置配置参数的其他方式(除了junit-platform.properties文件),请参阅此处。
您也可以提供自己的订购者。它必须实现ClassOrderer接口:
package foo;
public class MyOrderer implements ClassOrderer {
@Override
public void orderClasses(ClassOrdererContext context) {
Collections.shuffle(context.getClassDescriptors());
}
}
Run Code Online (Sandbox Code Playgroud)
junit.jupiter.testclass.order.default=foo.MyOrderer
Run Code Online (Sandbox Code Playgroud)
@Nested测试类不能按ClassOrderer.请参阅 JUnit 5文档和 ClassOrderer api 文档以了解更多信息。
从junit 5.8.0-M1版本开始有一个解决方案。
基本上你需要创建自己的排序者。我做了类似的事情。
这是您将在测试类中使用的注释:
@Retention(RetentionPolicy.RUNTIME)
public @interface TestClassesOrder {
public int value() default Integer.MAX_VALUE;
}
Run Code Online (Sandbox Code Playgroud)
然后您需要创建将实现 org.junit.jupiter.api.ClassOrderer 的类
public class AnnotationTestsOrderer implements ClassOrderer {
@Override
public void orderClasses(ClassOrdererContext context) {
Collections.sort(context.getClassDescriptors(), new Comparator<ClassDescriptor>() {
@Override
public int compare(ClassDescriptor o1, ClassDescriptor o2) {
TestClassesOrder a1 = o1.getTestClass().getDeclaredAnnotation(TestClassesOrder.class);
TestClassesOrder a2 = o2.getTestClass().getDeclaredAnnotation(TestClassesOrder.class);
if (a1 == null) {
return 1;
}
if (a2 == null) {
return -1;
}
if (a1.value() < a2.value()) {
return -1;
}
if (a1.value() == a2.value()) {
return 0;
}
if (a1.value() > a2.value()) {
return 1;
}
return 0;
}
});
}
}
Run Code Online (Sandbox Code Playgroud)
为了让它工作,你需要告诉 junit 你将使用哪个类来排序描述符。因此,您需要创建文件“ junit-platform.properties ”,它应该位于资源文件夹中。在该文件中,您只需要一行包含排序者类:
junit.jupiter.testclass.order.default=org.example.tests.AnnotationTestOrderer
Run Code Online (Sandbox Code Playgroud)
现在您可以像 Order 注释一样使用 orderer 注释,但在类级别:
@TestClassesOrder(1)
class Tests {...}
@TestClassesOrder(2)
class MainTests {...}
@TestClassesOrder(3)
class EndToEndTests {...}
Run Code Online (Sandbox Code Playgroud)
我希望这会对某人有所帮助。
在我们的项目中,我们创建了一些标记接口(例如
public interface SlowTestsCategory {}
Run Code Online (Sandbox Code Playgroud)
)
并将其放入测试速度慢的测试类中JUnit的@Category注释中。
@Category(SlowTestsCategory.class)
Run Code Online (Sandbox Code Playgroud)
之后,我们为 Gradle 创建了一些特殊任务,以按类别或按自定义顺序运行几个类别的测试:
task unitTest(type: Test) {
description = 'description.'
group = 'groupName'
useJUnit {
includeCategories 'package.SlowTestsCategory'
excludeCategories 'package.ExcludedCategory'
}
}
Run Code Online (Sandbox Code Playgroud)
该解决方案由 Gradle 提供,但也许它会对您有所帮助。
我给出了尝试的答案组合:
第二个答案基于classes该github项目的内容,可以通过BSD-2许可获得。
我定义了一些测试类:
public class LongRunningTest {
@Test
public void test() {
System.out.println(Thread.currentThread().getName() + ":\tlong test - started");
long time = System.currentTimeMillis();
do {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
} while(System.currentTimeMillis() - time < 1000);
System.out.println(Thread.currentThread().getName() + ":\tlong test - done");
}
}
Run Code Online (Sandbox Code Playgroud)
@Concurrent
public class FastRunningTest1 {
@Test
public void test1() {
try {
Thread.sleep(250);
} catch (InterruptedException e) {
}
System.out.println(Thread.currentThread().getName() + ":\tfrt1-test1 - done");
}
// +7 more repetions of the same method
}
Run Code Online (Sandbox Code Playgroud)
然后,我定义了测试套件:
(FastRunningTest2是具有调整后的输出的第一类的副本)
@SuiteClasses({LongRunningTest.class, LongRunningTest.class})
@RunWith(Suite.class)
public class SuiteOne {}
@SuiteClasses({FastRunningTest1.class, FastRunningTest2.class})
@RunWith(Suite.class)
public class SuiteTwo {}
@SuiteClasses({SuiteOne.class, SuiteTwo.class})
@RunWith(ConcurrentSuite.class)
public class TopLevelSuite {}
Run Code Online (Sandbox Code Playgroud)
当我执行时,TopLevelSuite我得到以下输出:
TopLevelSuite-1-thread-1: long test - started
FastRunningTest1-1-thread-4: frt1-test4 - done
FastRunningTest1-1-thread-2: frt1-test2 - done
FastRunningTest1-1-thread-1: frt1-test1 - done
FastRunningTest1-1-thread-3: frt1-test3 - done
FastRunningTest1-1-thread-5: frt1-test5 - done
FastRunningTest1-1-thread-3: frt1-test6 - done
FastRunningTest1-1-thread-1: frt1-test8 - done
FastRunningTest1-1-thread-5: frt1-test7 - done
FastRunningTest2-2-thread-1: frt2-test1 - done
FastRunningTest2-2-thread-2: frt2-test2 - done
FastRunningTest2-2-thread-5: frt2-test5 - done
FastRunningTest2-2-thread-3: frt2-test3 - done
FastRunningTest2-2-thread-4: frt2-test4 - done
TopLevelSuite-1-thread-1: long test - done
TopLevelSuite-1-thread-1: long test - started
FastRunningTest2-2-thread-5: frt2-test8 - done
FastRunningTest2-2-thread-2: frt2-test6 - done
FastRunningTest2-2-thread-1: frt2-test7 - done
TopLevelSuite-1-thread-1: long test - done
Which basically shows that the LongRunningTest is executed in parralel to the FastRunningTests. The default value of threads used for parallel execution defined by the Concurrent Annotation is 5, which can be seen in the output of the parallel execution of the FastRunningTests.
The downside is that theses Threads are not shared between FastRunningTest1 and FastRunningTest2.
This behavious shows that it is "somewhat" possible to do what you want to do (so whether that works with your current setup is a different question).
Also I am not sure whether this is actually worth the effort,
TestSuites manually (or write something that autogenerates them)threads for each class)As this basically shows that it is possible to define the execution order of classes and trigger their parallel execution, it should also be possibly to get the whole process to only use one ThreadPool (but I am not sure what the implication of that would be).
As the whole concept is based on a ThreadPoolExecutor, using a PriorityBlockingQueue which gives long running tasks a higher priority you would get closer to your ideal outcome of executing the long running tests first.
I experimented around a bit more and implemented my own custom suite runner and junit runner. The idea behind is to have your JUnitRunner submit the tests into a queue which is handeld by a single ThreadPoolExecutor. Because I didn't implement a blocking operation in the RunnerScheduler#finish method, I ended up with a solution where the tests from all classes were passed to the queue before the execution even started. (That might look different if there a more test classes and methods involved).
At least it proves the point that you can mess with junit at this level if you really want to.
The code of my poc is a bit messy and to lengthy to put it here, but if someone is interested I can push it into a github project.
在我提供建议之前,让我总结一下。
目前,我们确信我们在获得快速反馈方面没有问题。但是我们仍然希望更快地运行集成测试。我会推荐以下解决方案:
我处理过许多不同的项目(其中一些项目的 CI 构建运行了 48 小时),前 3 个步骤就足够了(即使是疯狂的情况)。很少需要第 4 步进行良好的测试。第 5 步适用于非常特殊的情况。
您会看到我的建议与流程相关,而不是与工具相关,因为问题出在流程中。
人们经常忽略根本原因并尝试调整工具(在这种情况下为 Maven)。他们获得了外观上的改进,但创建的解决方案的维护成本很高。
| 归档时间: |
|
| 查看次数: |
503 次 |
| 最近记录: |