在 Eclipse 的 JUnit 视图中排序单元测试

Posted

技术标签:

【中文标题】在 Eclipse 的 JUnit 视图中排序单元测试【英文标题】:Ordering unit tests in Eclipse's JUnit view 【发布时间】:2010-10-05 11:52:56 【问题描述】:

Eclipse 中的 JUnit 视图似乎对测试进行随机排序。如何按类名排序?

【问题讨论】:

这让我在 Eclipse 中发疯,我希望有人能将此作为选项添加到插件中。我希望接受的答案确实解决了问题! 【参考方案1】:

11 年后,JUnit 视图确实有名称排序,并且刚刚获得了“执行时间”排序。 见Eclipse 4.17 (2020-09)

按执行时间排序测试结果

JUnit 视图现在提供按执行时间对结果进行排序的能力。

默认情况下,结果将按执行顺序排序。 在所有测试完成后,从 JUnit View 菜单中选择 Sort By > Execution Time 将对结果重新排序。

当测试仍在运行时,它们将按执行顺序显示。

按执行顺序排序得到:

【讨论】:

【参考方案2】:

JUnit 视图中的排序测试已归档为bug #386453 in Eclipse Bugzilla。在那里发表评论和/或投票可能有助于更清楚地了解这个问题。

【讨论】:

【参考方案3】:

人们可能会做的一件事是使用 JUnit 3.x 的模式。我们使用了一个名为 AllTests 的测试套件,您可以在其中按特定顺序将测试添加到其中。对于每个包裹,我们都会得到另一个 AllTests。为这些测试套件提供与包相同的名称,可以轻松构建 junit 插件应该重视的层次结构。

我真的不喜欢它在 Junit 查看器中呈现测试方法的方式。它应该与它们在 TestCase 类中指定的顺序完全相同。我以重要性和特征的方式对这些方法进行排序。所以最失败的方法是先更正,然后在测试用例的后面部分更特殊。

测试运行者正在扰乱那些真的很烦人。我会自己看一下,如果找到解决方案,我会更新这个答案。

更新:

我对 TestCase 中方法名称排序的问题与此有关: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7023180(感谢甲骨文!)。

所以最后 oracle 改变了 class.getMethods 或 class.getDeclaredMethods 调用中方法的顺序。现在这些方法是随机的,并且可以在 JVM 的不同运行之间改变。它似乎与比较的优化有关,甚至是一种压缩方法名称的尝试-谁知道...。

那么还剩下什么。第一个可以使用:@FixMethodOrder(来自javacodegeeks.com):

    @FixMethodOrder(MethodSorters.DEFAULT) – 基于内部比较器的确定顺序 @FixMethodOrder(MethodSorters.NAME_ASCENDING) – 方法名的升序 @FixMethodOrder(MethodSorters.JVM) – pre 4.11 依赖于基于反射的顺序的方式

这很愚蠢,但可以解释为什么人们开始使用 test1TestName 架构。

更新2

我使用 ASM,因为 Javassist 还在 getMethods() 上生成随机排序的方法。他们在内部使用地图。对于 ASM,我只使用访客。

package org.junit.runners.model;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;

import com.flirtbox.ioc.OrderTest;

/**
 * @author Martin Kersten
*/
public class TestClassUtil 
public static class MyClassVisitor extends ClassVisitor 
    private final List<String> names;
    public MyClassVisitor(List<String> names) 
        super(Opcodes.ASM4);
        this.names = names;
    

    @Override
    public MethodVisitor visitMethod(int access, String name, String desc,
            String signature, String[] exceptions) 
        names.add(name);
        return super.visitMethod(access, name, desc, signature, exceptions);
    


private static List<String> getMethodNamesInCorrectOrder(Class<?> clazz) throws IOException 
    InputStream in = OrderTest.class.getResourceAsStream("/" + clazz.getName().replace('.', '/') + ".class");
    ClassReader classReader=new ClassReader(in);
    List<String> methodNames = new ArrayList<>();
    classReader.accept(new MyClassVisitor(methodNames), 0);
    return methodNames;


public static void sort(Class<?> fClass, List<FrameworkMethod> list) 
    try 
        final List<String> names = getMethodNamesInCorrectOrder(fClass);
        Collections.sort(list, new Comparator<FrameworkMethod>() 
            @Override
            public int compare(FrameworkMethod methodA, FrameworkMethod methodB) 
                int indexA = names.indexOf(methodA.getName());
                int indexB = names.indexOf(methodB.getName());
                if(indexA == -1)
                    indexA = names.size();
                if(indexB == -1)
                    indexB = names.size();
                return indexA - indexB;
            
        );
     catch (IOException e) 
        throw new RuntimeException("Could not optain the method names of " + fClass.getName() + " in correct order", e);
    


只需将它放在 org.junit.runners.model 包中的 src/test/java 文件夹中即可。现在将 junit 4.5 lib 的 org.junit.runners.model.TestClass 复制到同一个包中,并通过添加排序例程来更改其构造函数。

 public TestClass(Class<?> klass) 
    fClass= klass;
    if (klass != null && klass.getConstructors().length > 1)
        throw new IllegalArgumentException(
                "Test class can only have one constructor");

    for (Class<?> eachClass : getSuperClasses(fClass))
        for (Method eachMethod : eachClass.getDeclaredMethods())
            addToAnnotationLists(new FrameworkMethod(eachMethod));

            //New Part
    for(List<FrameworkMethod> list : fMethodsForAnnotations.values()) 
        TestClassUtil.sort(fClass, list);
    

    //Remove once you have verified the class is really picked up
    System.out.println("New TestClass for " + klass.getName());


给你。现在,您已经按照在 java 文件中声明的顺序对方法进行了很好的排序。如果您想知道类路径通常设置为类加载器首先考虑 src(目标或 bin)文件夹中的所有内容。因此,在定义相同的包和相同的类时,您可以“覆盖”您使用的任何库中的每个类/接口。这就是诀窍!

更新3 我能够以正确的顺序获得每个包和每个类的树视图。

想法是将 ParentRunner 子类化,然后将所有您认为是公共的类添加到其中并且具有使用 test 注释的方法。 添加一个 getName() 方法,仅返回套件运行程序所代表的类的包名称(因此您可以将树视为没有套件类名称的包树)。 如果找到某个套件类,请检查子目录(我对所有套件类都使用 AllTests)。 如果您在子目录中找不到套件类,请检查其所有子目录,这样如果父目录不包含套件,您就不会错过包含测试的包。

就是这样。我到处添加的套件类是: @RunWith(MySuiteRunner.class) public class AllTests

就是这样。你应该付出足够的努力来开始和扩展这个。套件运行器仅使用反射,但我按字母顺序对子目录的测试类和套件进行排序,并且子目录套件(代表它们所在的包)排在最前面。

【讨论】:

【参考方案4】:

我也在寻找解决方案,我从下面的 URL 中找到了一种破解。我不知道它是否适合你,但它在 Spring Tool Suite 2.5.2 中对我有用。

http://osdir.com/ml/java.junit.user/2002-10/msg00077.html

【讨论】:

【参考方案5】:

正如 Gary 在 cmets 中所说:

如果 Unit Runner 可以 被告知继续并订购它们 班级名称。嗯,也许我应该看看 进入源代码...

我确实看过,但没有提示对这些名称进行排序的功能。我建议对 JUnit 插件提出更改请求,但我不认为有很多人在使用这个东西,所以:DIY。

如果你修改插件代码,我想看看解决方案。

【讨论】:

是的,可能最好的解决方案是修改插件代码。希望我有时间。【参考方案6】:

标记写道:

它根据执行时间对它们进行排序, 也许你应该对你的方法进行排序? 来源/排序成员

标记是正确的。但是你不能对你的单元测试进行排序。执行顺序不可推测。

单元测试必须独立构建,并且是随机的,UnitRunner 调用它们的方式。

在大多数情况下,测试方法按字母顺序排序。班级是随机的。尝试使用 TestSuite 来订购您的测试。

【讨论】:

好吧,我尽量避免使用TestSuite。我很欣赏单元测试的独立性,并且在创建它们时我坚持这一点,但是如果可以告诉 Unit Runner 继续并按类名对它们进行排序,那就太好了。嗯,也许我应该看看源代码......【参考方案7】:

如果您确实需要 JUnit 测试之间的硬依赖,请尝试 JExample extension

JExample 将生产者-消费者关系引入单元测试。 生产者是一种测试方法,它产生其被测单元作为返回值。 消费者是一种依赖于一个或多个生产者及其返回值的测试方法。

对于 Junit4.4 或 4.5,您可以 install it in Eclipse。

import jexample.Depends;

  @Test
  @Depends("#testEmpty") 
  public Stack<Integer> testPush(Stack<Integer> $)  
      $.push(42); 
      assertFalse($.isEmpty()); 
      return $; 
   

正如这篇 IBM 文章中提到的 "In pursuit of code quality: JUnit 4 vs. TestNG":

JUnit 框架试图实现的一件事是测试隔离。 不利的一面是,这使得指定测试用例执行的顺序变得非常困难,这对于任何类型的依赖测试都是必不可少的。 开发人员使用了不同的技术来解决这个问题,例如按字母顺序指定测试用例或严重依赖固定装置 (@Before@After) 来正确设置。

这些变通方法对于成功的测试很好,但对于失败的测试,它们有一个不便的后果:每个后续的依赖测试也会失败。在某些情况下,这可能会导致大型测试套件报告不必要的失败

因此请注意:如果您保留任何解决方案以按您想要的方式订购 JUnit 测试...您需要考虑该解决方案是否支持“跳过”功能,以便即使其中一个测试失败也允许其他测试继续进行.

【讨论】:

我想他只是想按特定顺序查看结果 ...是的,因此我的回答是:由于您无法对结果进行排序,因此您可以尝试订购测试。 是的,只是结果。只是因为我可能想在单元测试运行后寻找特定的测试结果。我不确定为什么 Eclipse 不能在运行测试结果时对其进行排序。只需使用 SortedSet。

以上是关于在 Eclipse 的 JUnit 视图中排序单元测试的主要内容,如果未能解决你的问题,请参考以下文章

[转]在Eclipse中使用JUnit4进行单元测试(初级篇)

在Eclipse中使用JUnit4进行单元测试(中级篇)

在Eclipse中使用JUnit4进行单元测试(中级篇)

在Eclipse中使用JUnit4进行单元测试(高级篇)

软件测试——JUnit基础

[转]在Eclipse中使用JUnit4进行单元测试(高级篇)