JUnit 中测试的顺序
最后更新:2017年11月26日
1. 概述
默认情况下,JUnit 使用确定性的但不可预测的顺序运行测试 (MethodSorters.DEFAULT)。
在大多数情况下,这种行为完全可以接受。但有时我们需要强制执行特定顺序。
2. JUnit 5 中测试方法的排序
在 JUnit 5 中,我们可以使用 @TestMethodOrder 来控制测试的执行顺序。
我们可以使用我们自己的 MethodOrderer,如稍后所示。
或者我们可以选择三个内置排序器中的一个
- 字母数字 排序
- @Order 注解
- 随机顺序
2.1. 使用 字母数字 排序
JUnit 5 包含一组内置的 MethodOrderer 实现,以字母数字顺序运行测试。
例如,它提供了 MethodOrderer.MethodName 来 根据其名称和形式参数列表对测试方法进行排序
@TestMethodOrder(MethodOrderer.MethodName.class)
public class AlphanumericOrderUnitTest {
private static StringBuilder output = new StringBuilder("");
@Test
void myATest() {
output.append("A");
}
@Test
void myBTest() {
output.append("B");
}
@Test
void myaTest() {
output.append("a");
}
@AfterAll
public static void assertOutput() {
assertEquals("ABa", output.toString());
}
}
类似地,我们可以使用 MethodOrderer.DisplayName 来 根据其显示名称按字母数字顺序对方法进行排序。
请记住 MethodOrderer.Alphanumeric 是另一种选择。但是,此实现已被弃用,将在 6.0 中删除。
2.2. 使用 @Order 注解
我们可以使用 @Order 注解来强制测试以特定顺序运行。
在下面的示例中,方法将运行 firstTest(),然后 secondTest(),最后 thirdTest()
@TestMethodOrder(OrderAnnotation.class)
public class OrderAnnotationUnitTest {
private static StringBuilder output = new StringBuilder("");
@Test
@Order(1)
void firstTest() {
output.append("a");
}
@Test
@Order(2)
void secondTest() {
output.append("b");
}
@Test
@Order(3)
void thirdTest() {
output.append("c");
}
@AfterAll
public static void assertOutput() {
assertEquals("abc", output.toString());
}
}
2.3. 使用 随机顺序
我们还可以使用 MethodOrderer.Random 实现以伪随机方式对测试方法进行排序
@TestMethodOrder(MethodOrderer.Random.class)
public class RandomOrderUnitTest {
private static StringBuilder output = new StringBuilder("");
@Test
void myATest() {
output.append("A");
}
@Test
void myBTest() {
output.append("B");
}
@Test
void myCTest() {
output.append("C");
}
@AfterAll
public static void assertOutput() {
assertEquals("ACB", output.toString());
}
}
事实上,JUnit 5 使用 System.nanoTime() 作为默认种子来对测试方法进行排序。 这意味着方法的执行顺序在可重复测试中可能不相同。
但是,我们可以使用 junit.jupiter.execution.order.random.seed 属性配置自定义种子以创建可重复构建。
我们可以在 junit-platform.properties 文件中指定自定义种子的值
junit.jupiter.execution.order.random.seed=100
2.4. 使用自定义顺序
最后,我们可以通过实现 MethodOrderer 接口来使用我们自己的顺序。
在我们的 CustomOrder 中,我们将根据测试名称以不区分大小写的字母数字顺序对测试进行排序
public class CustomOrder implements MethodOrderer {
@Override
public void orderMethods(MethodOrdererContext context) {
context.getMethodDescriptors().sort(
(MethodDescriptor m1, MethodDescriptor m2)->
m1.getMethod().getName().compareToIgnoreCase(m2.getMethod().getName()));
}
}
然后我们将使用 CustomOrder 以 myATest(), myaTest(),最后 myBTest() 的顺序运行我们之前示例中的相同测试
@TestMethodOrder(CustomOrder.class)
public class CustomOrderUnitTest {
// ...
@AfterAll
public static void assertOutput() {
assertEquals("AaB", output.toString());
}
}
2.5. 设置默认顺序
JUnit 5 提供了通过 junit.jupiter.testmethod.order.default 参数设置默认方法排序器的便捷方法。
同样,我们可以在 junit-platform.properties 文件中配置我们的参数
junit.jupiter.testmethod.order.default = org.junit.jupiter.api.MethodOrderer$DisplayName
默认排序器将应用于所有未通过 @TestMethodOrder 限定的测试。
另一件重要的事情是,指定的类必须实现 MethodOrderer 接口。
3. JUnit 4 中测试方法的排序
对于仍然使用 JUnit 4 的人来说,用于排序测试的 API 略有不同。
让我们也来看看如何在以前的版本中实现这些选项。
3.1. 使用 MethodSorters.DEFAULT
这种默认策略使用它们的哈希码来比较测试方法。
如果发生哈希冲突,则使用词法顺序
@FixMethodOrder(MethodSorters.DEFAULT)
public class DefaultOrderOfExecutionTest {
private static StringBuilder output = new StringBuilder("");
@Test
public void secondTest() {
output.append("b");
}
@Test
public void thirdTest() {
output.append("c");
}
@Test
public void firstTest() {
output.append("a");
}
@AfterClass
public static void assertOutput() {
assertEquals(output.toString(), "cab");
}
}
当我们运行上面的类中的测试时,我们会看到它们都通过了,包括 assertOutput()。
3.2. 使用 MethodSorters.JVM
另一种排序策略是 MethodSorters.JVM。
这种策略利用自然的 JVM 排序,每次运行都可能不同:
@FixMethodOrder(MethodSorters.JVM)
public class JVMOrderOfExecutionTest {
// same as above
}
每次我们运行此类中的测试时,都会得到不同的结果。
3.3. 使用 MethodSorters.NAME_ASCENDING
最后,可以使用这种策略以词法顺序运行测试
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class NameAscendingOrderOfExecutionTest {
// same as above
@AfterClass
public static void assertOutput() {
assertEquals(output.toString(), "abc");
}
}
当我们运行此类中的测试时,我们会看到它们都通过了,包括 assertOutput()。这证实了我们使用注解设置的执行顺序。
4. 使用 @TestClassOrder 对测试类进行排序
我们还可以使用 @TestClassOrder 控制测试类的执行顺序。
Junit 5 带有 ClassOrderer 接口,类似于 MethodOrderer。我们可以像在前面的章节中使用方法排序一样使用它。 ClassOrderer 支持以下测试排序方式
- ClassName – 根据类名对类进行字母数字排序
- DisplayName – 根据显示名称对类进行字母数字排序
- OrderAnnotation – 根据 @Order 注解对类进行排序
- Random – 对类进行随机排序
- 自定义顺序 – 根据自定义排序顺序对类进行排序
4.1. 使用 ClassName
它能够按照类别的完全限定类名以字母顺序执行类。 这是基于代码中实际类名的默认排序机制。
让我们定义一些我们将用于运行不同测试的测试类
public class TestA {
@Test
void testA() {
System.out.println("Running TestA");
}
}
public class TestB {
@Test
void testB() {
System.out.println("Running TestB");
}
}
public class TestC {
@Test
void testC() {
System.out.println("Running TestC");
}
}
要使用 @TestClassOrder, 类需要属于同一个层次结构。为此,通过使用 @Nested 和 继承,我们创建了一个灵活的结构,允许我们将测试在套件中进行逻辑分组
@TestClassOrder(ClassOrderer.ClassName.class)
public class ClassNameOrderUnitTest {
@Nested
class C extends TestC {}
@Nested
class B extends TestB {}
@Nested
class A extends TestA {}
}
在上面的示例中,我们创建了一个父测试套件,ClassNameOrderUnitTest,它充当测试类的容器。
@TestClassOrder(ClassOrderer.ClassName.class) 允许我们强制执行字母顺序。运行程序后,输出将是
Running TestA
Running TestB
Running TestC
4.2. 使用 DisplayName
ClassOrderer.DisplayName 如果显式设置,则按其显示名称对类进行排序。 如果我们没有设置任何显示名称,JUnit 将回退到类名。
在下面的示例中,我们将在嵌套包装类上设置 @DisplayName 注解,而不是在原始测试类 TestA、TestB 和 TestC 上:
@TestClassOrder(ClassOrderer.DisplayName.class)
public class DisplayNameOrderUnitTest {
@Nested
@DisplayName("Class C")
class Z extends TestC {}
@Nested
@DisplayName("Class B")
class A extends TestA {}
@Nested
@DisplayName("Class A")
class B extends TestB {}
}
对于上面的程序,JUnit 将按字母顺序对这些显示名称进行排序。 显示名称的排序顺序将是“Class A”、“Class B”和“Class C”,这会导致以下输出
Running TestB
Running TestA
Running TestC
4.3. 使用 OrderAnnotation
@Order 注解允许我们显式定义测试套件中执行测试类的顺序。 当我们想要对执行顺序进行一些显式控制或需要以特定顺序运行测试时,它很有用。
在下面的示例中,@Order 注解指定了嵌套类的优先级
@TestClassOrder(ClassOrderer.OrderAnnotation.class)
public class OrderAnnotationUnitTest {
@Nested
@Order(3)
class A extends TestA {}
@Nested
@Order(1)
class B extends TestB {}
@Nested
@Order(2)
class C extends TestC {}
}
具有较低@Order值的类将首先执行。因此,我们得到以下输出
Running TestB
Running TestC
Running TestA
4.4. 随机
有时,我们希望以随机顺序运行测试,以检测测试之间的依赖关系。这使我们能够确保我们的测试不依赖于执行顺序。
ClassOrderer.Random.class允许我们配置套件,以随机化测试类的执行。 每次测试套件运行时,都会应用这种随机性
@TestClassOrder(ClassOrderer.Random.class)
public class RandomOrderUnitTest {
@Nested
class C extends TestC {}
@Nested
class B extends TestB {}
@Nested
class A extends TestA {}
}
当执行上述测试时,输出将以随机顺序显示,并且每次运行都会有所不同。
运行 1 输出
Running TestA
Running TestC
Running TestB
运行 2 输出
Running TestC
Running TestA
Running TestB
4.5. 使用自定义顺序
我们可以实现自己的ClassOrderer来定义基于元数据、类名长度、配置等标准来排序逻辑。ClassOrderer接口允许我们定义自定义逻辑来基于特定标准对测试类进行排序。
让我们实现一个自定义ClassOrderer,该ClassOrderer根据类名的长度对测试类进行排序
public class CustomClassOrderer implements ClassOrderer {
@Override
public void orderClasses(ClassOrdererContext context) {
context.getClassDescriptors().sort(
Comparator.comparingInt(descriptor ->
descriptor.getTestClass().getSimpleName().length()
)
);
}
}
让我们使用CustomClassOrderer根据类名的长度对测试类进行排序。排序顺序将从短到长
@TestClassOrder(CustomClassOrderer.class)
public class CustomOrderUnitTest {
@Nested
class Longest extends TestA {}
@Nested
class Middle extends TestB {}
@Nested
class Short extends TestC {}
}
输出按类名长度排序,即Longest、Middle和Short:
Running TestC
Running TestB
Running TestA
由于Short 在这些类中具有最短的名称,因此测试运行器首先执行它,导致“Running TestC”首先出现。随后,测试运行器按顺序执行Middle和Longest类。
5. 结论
在这篇简短的文章中,我们了解了JUnit中可用的设置执行顺序的方法。
支持本文的代码可在 GitHub 上获取。 一旦你以 Baeldung Pro 会员 身份登录,就开始学习并在项目上进行编码。















