我想按特定顺序执行由 @Test
注释的测试方法。
例如:
public class MyTest {
@Test public void test1(){}
@Test public void test2(){}
}
我想确保每次运行 MyTest
时在 test2()
之前运行 test1()
,但我找不到像 @Test(order=xx)
这样的注释。
我认为这对 JUnit 来说是非常重要的功能,如果 JUnit 的作者不想要订单功能,为什么?
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
注释类,为所有测试方法保留 @Test
注释并根据所需的执行顺序按字母顺序重命名它们,例如 t1_firstTest()
、t2_secondTest()
等。
我认为这对 JUnit 来说是非常重要的功能,如果 JUnit 的作者不想要订单功能,为什么?
我不确定 JUnit 是否有一种干净的方法可以做到这一点,据我所知,JUnit 假设所有测试都可以按任意顺序执行。从常见问题解答:
如何使用测试夹具? (...) 无法保证测试方法调用的顺序,因此 testOneItemCollection() 可能在 testEmptyCollection() 之前执行。 (...)
为什么会这样?好吧,我相信让测试顺序依赖是作者不想推广的一种做法。测试应该是独立的,它们不应该是耦合的,违反这一点会使事情更难维护,会破坏单独运行测试的能力(显然),等等。
话虽这么说,如果您真的想朝这个方向发展,请考虑使用 TestNG,因为它支持以任意顺序运行测试方法(例如指定方法取决于方法组)。 Cedric Beust 在 order of execution of tests in testng 中解释了如何做到这一点。
如果您摆脱现有的 Junit 实例,并在构建路径中下载 JUnit 4.11 或更高版本,则以下代码将按名称顺序执行测试方法,按升序排序:
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runners.MethodSorters;
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class SampleTest {
@Test
public void testAcreate() {
System.out.println("first");
}
@Test
public void testBupdate() {
System.out.println("second");
}
@Test
public void testCdelete() {
System.out.println("third");
}
}
@Inherited
定义(在 Junit 4.12 中),因此对我的 AbstractTestCase
父类无效。
迁移到 TestNG 似乎是最好的方法,但我在这里没有看到 jUnit 的明确解决方案。这是我为 jUnit 找到的最易读的解决方案/格式:
@FixMethodOrder( MethodSorters.NAME_ASCENDING ) // force name ordering
public class SampleTest {
@Test
void stage1_prepareAndTest(){};
@Test
void stage2_checkSomething(){};
@Test
void stage2_checkSomethingElse(){};
@Test
void stage3_thisDependsOnStage2(){};
@Test
void callTimeDoesntMatter(){}
}
这确保在 stage1 之后和 stage3 之前调用 stage2 方法。
PS 我觉得这种方法比 jUnit 5.5 @Order 注释更好,因为它为读者提供了更好的符号。
0
前缀,否则它将无法正常工作,例如 void stage01_prepareAndTest(){ }
如果订单很重要,您应该自己下订单。
@Test public void test1() { ... }
@Test public void test2() { test1(); ... }
特别是,如有必要,您应该列出一些或所有可能的顺序排列以进行测试。
例如,
void test1();
void test2();
void test3();
@Test
public void testOrder1() { test1(); test3(); }
@Test(expected = Exception.class)
public void testOrder2() { test2(); test3(); test1(); }
@Test(expected = NullPointerException.class)
public void testOrder3() { test3(); test1(); test2(); }
或者,对所有排列进行全面测试:
@Test
public void testAllOrders() {
for (Object[] sample: permute(1, 2, 3)) {
for (Object index: sample) {
switch (((Integer) index).intValue()) {
case 1: test1(); break;
case 2: test2(); break;
case 3: test3(); break;
}
}
}
}
这里,permute()
是一个简单的函数,它将所有可能的排列迭代到数组集合中。
test2
test1
再次 运行。 Junit 可能仍会在 test1
之前运行 test2
。这可能不是您想要的,也不是问题的有效答案。
从 5.5 开始的 JUnit 允许在类上使用 @TestMethodOrder(OrderAnnotation.class)
,在测试方法上使用 @Order(1)
。
JUnit 旧版本允许使用类注解对测试方法运行排序:
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@FixMethodOrder(MethodSorters.JVM)
@FixMethodOrder(MethodSorters.DEFAULT)
默认情况下,测试方法按字母顺序运行。因此,要设置特定方法的顺序,您可以将它们命名为:
a_TestWorkUnit_WithCertainState_ShouldDoSomething b_TestWorkUnit_WithCertainState_ShouldDoSomething c_TestWorkUnit_WithCertainState_ShouldDoSomething
或者
_1_TestWorkUnit_WithCertainState_ShouldDoSomething _2_TestWorkUnit_WithCertainState_ShouldDoSomething _3_TestWorkUnit_WithCertainState_ShouldDoSomething
您可以找到 examples here。
这是我在使用 Junit 时遇到的主要问题之一,我想出了以下对我来说很好的解决方案:
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import org.junit.runners.BlockJUnit4ClassRunner;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.InitializationError;
public class OrderedRunner extends BlockJUnit4ClassRunner {
public OrderedRunner(Class<?> clazz) throws InitializationError {
super(clazz);
}
@Override
protected List<FrameworkMethod> computeTestMethods() {
List<FrameworkMethod> list = super.computeTestMethods();
List<FrameworkMethod> copy = new ArrayList<FrameworkMethod>(list);
Collections.sort(copy, new Comparator<FrameworkMethod>() {
@Override
public int compare(FrameworkMethod f1, FrameworkMethod f2) {
Order o1 = f1.getAnnotation(Order.class);
Order o2 = f2.getAnnotation(Order.class);
if (o1 == null || o2 == null) {
return -1;
}
return o1.order() - o2.order();
}
});
return copy;
}
}
还创建一个如下界面:
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD})
public @interface Order {
public int order();
}
现在假设您有 A 类,您在其中编写了几个测试用例,如下所示:
(@runWith=OrderRunner.class)
Class A{
@Test
@Order(order = 1)
void method(){
//do something
}
}
所以执行将从名为“method()”的方法开始。谢谢!
JUnit 5 更新(和我的观点)
我认为这对 JUnit 来说是非常重要的功能,如果 JUnit 的作者不想要订单功能,为什么?
默认情况下,单元测试库不会尝试按照源文件中出现的顺序执行测试。
JUnit 5 作为 JUnit 4 以这种方式工作。为什么?因为如果顺序很重要,则意味着它们之间会耦合一些测试,这不适合单元测试。
因此,JUnit 5 引入的 @Nested
功能遵循相同的默认方法。
但对于集成测试,测试方法的顺序可能很重要,因为测试方法可能会以另一种测试方法所期望的方式更改应用程序的状态。
例如,当您编写集成时测试网店结账处理,第一个执行的测试方法是注册客户,第二个是在购物篮中添加商品,最后一个是结账。如果测试运行程序不遵守该顺序,则测试场景有缺陷并且将失败。
因此,在 JUnit 5(从 5.4 版本开始)中,您可以通过注释测试类来设置执行顺序使用 @TestMethodOrder(OrderAnnotation.class)
并通过使用 @Order(numericOrderValue)
为顺序重要的方法指定顺序。
例如 :
@TestMethodOrder(OrderAnnotation.class)
class FooTest {
@Order(3)
@Test
void checkoutOrder() {
System.out.println("checkoutOrder");
}
@Order(2)
@Test
void addItemsInBasket() {
System.out.println("addItemsInBasket");
}
@Order(1)
@Test
void createUserAndLogin() {
System.out.println("createUserAndLogin");
}
}
输出 :
createUserAndLogin addItemsInBasket checkoutOrder
顺便说一句,指定 @TestMethodOrder(OrderAnnotation.class)
似乎不需要(至少在我测试的 5.4.0 版本中)。
旁注关于这个问题:JUnit 5 是编写集成测试的最佳选择吗?我不认为它应该是第一个考虑的工具(Cucumber 和 co 可能经常为集成测试带来更具体的价值和特性)但是在一些集成测试用例中,JUnit 框架就足够了。因此,该功能存在是一个好消息。
(尚未发布的)更改 https://github.com/junit-team/junit/pull/386 引入了 @SortMethodsWith
。 https://github.com/junit-team/junit/pull/293 至少在没有它的情况下使顺序可预测(在 Java 7 中它可能是非常随机的)。
查看 JUnit 报告。 JUnit 已经按包组织。每个包都有(或可以有)TestSuite 类,每个类依次运行多个 TestCases。每个 TestCase 可以有多个 public void test*()
形式的测试方法,每个测试方法实际上都将成为它们所属的 TestCase 类的一个实例。每个测试方法(TestCase 实例)都有一个名称和一个通过/失败标准。
我的管理层需要的是单个 TestStep 项目的概念,每个项目都报告自己的通过/失败标准。任何测试步骤的失败都不能阻止后续测试步骤的执行。
过去,我所在位置的测试开发人员将 TestCase 类组织到与被测产品的各个部分相对应的包中,为每个测试创建一个 TestCase 类,并使每个测试方法成为测试中的一个单独的“步骤”,在 JUnit 输出中完成自己的通过/失败标准。每个 TestCase 都是一个独立的“测试”,但是 TestCase 中的各个方法或测试“步骤”必须以特定的顺序出现。
TestCase 方法是 TestCase 的步骤,并且测试设计者在每个测试步骤中获得了单独的通过/失败标准。现在测试步骤很混乱,测试(当然)失败了。
例如:
Class testStateChanges extends TestCase
public void testCreateObjectPlacesTheObjectInStateA()
public void testTransitionToStateBAndValidateStateB()
public void testTransitionToStateCAndValidateStateC()
public void testTryToDeleteObjectinStateCAndValidateObjectStillExists()
public void testTransitionToStateAAndValidateStateA()
public void testDeleteObjectInStateAAndObjectDoesNotExist()
public void cleanupIfAnythingWentWrong()
每个测试方法都断言并报告其自己的单独的通过/失败标准。为了排序而将其折叠成“一个大测试方法”会丢失 JUnit 摘要报告中每个“步骤”的通过/失败标准粒度。 ...这让我的经理们感到不安。他们目前正在要求另一种选择。
任何人都可以解释一个带有乱序测试方法排序的 JUnit 如何支持每个连续测试步骤的单独的通过/失败标准,如上例所示并由我的管理层要求?
无论文档如何,我都认为这是 JUnit 框架的严重退化,这让许多测试开发人员的生活变得困难。
不确定我是否同意,如果我想测试“文件上传”然后测试“文件上传插入的数据”,为什么我不希望这些相互独立?我认为能够单独运行它们而不是在 Goliath 测试用例中同时运行它们是完全合理的。
当测试用例作为一个套件运行时,您想要的是完全合理的。
不幸的是,现在没有时间给出一个完整的解决方案,但是看看类:
org.junit.runners.Suite
它允许您以特定顺序调用测试用例(从任何测试类)。
这些可用于创建功能、集成或系统测试。
这会使您的单元测试保持原样,没有特定的顺序(如推荐的那样),无论您是否像那样运行它们,然后重新使用测试作为更大图景的一部分。
我们为单元、集成和系统测试重用/继承相同的代码,有时是数据驱动的,有时是提交驱动的,有时作为一个套件运行。
在此处查看我的解决方案:“Junit 和 java 7”。
在本文中,我描述了如何按顺序运行 junit 测试——“就像在你的源代码中一样”。测试将按照您的测试方法出现在类文件中的顺序运行。
http://intellijava.blogspot.com/2012/05/junit-and-java-7.html
但正如 Pascal Thivent 所说,这不是一个好的做法。
使用 JUnit 5.4,您可以指定顺序:
@Test
@Order(2)
public void sendEmailTestGmail() throws MessagingException {
你只需要注释你的类
@TestMethodOrder(OrderAnnotation.class)
https://junit.org/junit5/docs/current/user-guide/#writing-tests-test-execution-order
我在我的项目中使用它并且效果很好!
您可以使用以下代码之一:@FixMethodOrder(MethodSorters.JVM)
OR @FixMethodOrder(MethodSorters.DEFAULT)
OR @FixMethodOrder(MethodSorters.NAME_ASCENDING)
在你的测试课之前是这样的:
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class BookTest {...}
正如其他人所说,理想情况下,测试应该独立于执行顺序。这使得测试不那么脆弱,并允许它们独立运行(许多 IDE 允许您选择一种测试方法并独立于其他测试执行它)。
话虽如此,对于集成测试,有些人更喜欢依赖方法排序。
从 JUnit 4.13 开始,您可以定义自己的类来通过扩展 Ordering
来重新排序测试。有关详细信息,请参阅 the JUnit wiki。下面是一个使用内置 Alphanumeric
类使用测试方法名称按字母数字顺序排列测试的示例:
import org.junit.Test;
import org.junit.runner.OrderWith;
import org.junit.runner.manipulation.Alphanumeric;
@OrderWith(Alphanumeric.class)
public class TestMethodOrder {
@Test
public void testA() {
System.out.println("first");
}
@Test
public void testB() {
System.out.println("second");
}
@Test
public void testC() {
System.out.println("third");
}
}
是时候迁移到 Junit5 了。这是我们可以获得的示例:
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
class OrderedTests {
@Test
@Order(1)
void nullValues() {}
@Test
@Order(2)
void emptyValues() {}
@Test
@Order(3)
void validValues() {}
}
对于 Junit4,将您在多个测试中的逻辑复制到一种测试方法中。
对于 JUnit 4,将这个注解放在测试类上解决了这个问题。
@FixMethodOrder(MethodSorters.JVM)
JUnit 4 更新
从 JUnit 4.13 @OrderWith
开始,可以重现 JUnit 5 @Order
注释。此解决方案与 JUnit 4 的集成比 @RunWith
自定义 BlockJUnit4ClassRunner
实现更好。
以下是我如何用注解排序替换方法名称排序 (@FixMethodOrder(MethodSorters.NAME_ASCENDING)
)。
@OrderWith(OrderAnnotation.class)
public class MyTest {
@Test
@Order(-1)
public void runBeforeNotAnnotatedTests() {}
@Test
public void notAnnotatedTestHasPriority0() {}
@Test
@Order(1)
public void thisTestHasPriority1() {}
@Test
@Order(2)
public void thisTestHasPriority2() {}
}
/**
* JUnit 4 equivalent of JUnit 5's {@code org.junit.jupiter.api.Order}
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD })
public @interface Order {
/**
* Default order value for elements not explicitly annotated with {@code @Order}.
*
* @see Order#value
*/
int DEFAULT = 0;
/**
* The order value for the annotated element.
* <p>Elements are ordered based on priority where a lower value has greater
* priority than a higher value. For example, {@link Integer#MAX_VALUE} has
* the lowest priority.
*
* @see #DEFAULT
*/
int value();
}
import org.junit.runner.Description;
import org.junit.runner.manipulation.Ordering;
import org.junit.runner.manipulation.Sorter;
/**
* Order test methods by their {@link Order} annotation. The lower value has the highest priority.
* The tests that are not annotated get the default value {@link Order#DEFAULT}.
*/
public class OrderAnnotation extends Sorter implements Ordering.Factory {
public OrderAnnotation() {
super(COMPARATOR);
}
@Override
public Ordering create(Context context) {
return this;
}
private static final Comparator<Description> COMPARATOR = Comparator.comparingInt(
description -> Optional.ofNullable(description.getAnnotation(Order.class))
.map(Order::value)
.orElse(Order.DEFAULT));
}
未注释的测试的默认优先级为 0。具有相同优先级的测试的顺序未确定。
要点:https://gist.github.com/jcarsique/df98e0bad9e88e8258c4ab34dad3c863
灵感来自:
阿曼高尔的回答
JUnit 团队的测试执行顺序 Wiki
JUnit 5 源代码
我已经阅读了一些答案并同意它不是最佳实践,而是订购测试的最简单方法 - JUnit 默认运行测试的方式是按字母名称升序。
因此,只需按您想要的字母顺序命名您的测试。另请注意,测试名称必须以单词 test 开头。只需注意数字
test12 将在 test2 之前运行
所以:
testA_MyFirstTest testC_ThirdTest testB_ATestThatRunsSecond
请查看这个:https://github.com/TransparentMarket/junit。它按照指定的顺序运行测试(在编译的类文件中定义)。它还具有一个 AllTests 套件,可以首先运行由子包定义的测试。使用 AllTests 实现,可以扩展解决方案以过滤属性(我们曾经使用 @Fast 注释,但尚未发布)。
以下是可以产生所需行为的 JUnit 扩展:https://github.com/aafuks/aaf-junit
我知道这与 JUnit 哲学的作者背道而驰,但是当在非严格单元测试的环境中使用 JUnit 时(如在 Java 中实践的那样),这可能会非常有帮助。
最后我认为我的测试没有按顺序运行,但事实是我的异步工作是一团糟。处理并发时,您还需要在测试之间执行并发检查。在我的例子中,作业和测试共享一个信号量,所以下一个测试会挂起,直到正在运行的作业释放锁。
我知道这与这个问题并不完全相关,但也许可以帮助解决正确的问题
如果您想在 JUnit 5 中按特定顺序运行测试方法,可以使用以下代码。
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class MyClassTest {
@Test
@Order(1)
public void test1() {}
@Test
@Order(2)
public void test2() {}
}
不定期副业成功案例分享