开发者社区 问答 正文

使用Jupiter进行Java参数化单元测试:更好地参考测试数据集

对于基于gradle的侧轨项目,我从junit4移到了junit5(jupiter),这在编写参数化测试时提供了更大的灵活性。那很棒。

但是,有一个烦人的细节使调试测试变得繁琐:与Intellij IDEA的测试运行程序不同,gradle测试运行程序将所有测试用例结果可视化为列表,但是列表的条目(因此测试用例)由数据的数字键引用。在数据源中设置参数化测试的条目。因此,实际测试数据不像IDEA的测试运行程序那样。哪个并不能真正帮助您理解许多测试用例中的哪个失败。

我知道这是我面临的一个问题,因为要努力使测试顺利进行。使用IDEA自己的测试运行器时一切正常。但是,我还是犹豫使用那个:我使用gradle的原因是我使用了外部构建管道。使用两个不同的测试跑步者闻起来就像必须处理不同的测试结果...

所以我的问题是:如何让gradle测试运行者使用实际测试数据作为测试用例的参考?与IDEA的测试运行程序相似吗?我尝试使用地图,但是jupiter解释说它无法流式传输。...我发现的唯一解决方法是将一些数据集标识符输出到StdOut,但该标识符埋在其余的输出中。有人可以告诉我如何以更优雅的方式实现这一目标吗?理想地如下面的示例所示?

一个例子:

@ParameterizedTest
@MethodSource("allFactoredClasses")
public void clearInstances_shouldClearInstances(Class<? extends Factored> factoredClass) {
    // ...
}

static private Set<Class<? extends Factored>> allFactoredClasses() {
    Reflections reflections = new Reflections("com.example.project");
    return reflections.getSubTypesOf(Factored.class);
}

这是实际的可视化效果(已键入,因此我不必发布图像):

TestResults
  com.example.project.factory.FactoredTest
    v clearInstances_shouldClearInstances(Class)[1]
    v clearInstances_shouldClearInstances(Class)[2]
    v clearInstances_shouldClearInstances(Class)[3]
    x clearInstances_shouldClearInstances(Class)[4]
    v clearInstances_shouldClearInstances(Class)[5]
    v clearInstances_shouldClearInstances(Class)[6]
    v clearInstances_shouldClearInstances(Class)[7]

这是所需的可视化效果:

TestResults
  com.example.project.factory.FactoredTest
    v clearInstances_shouldClearInstances(Class)[Controller]
    v clearInstances_shouldClearInstances(Class)[Reader]
    v clearInstances_shouldClearInstances(Class)[Parser]
    x clearInstances_shouldClearInstances(Class)[Writer]
    v clearInstances_shouldClearInstances(Class)[Logger]
    v clearInstances_shouldClearInstances(Class)[Filter]
    v clearInstances_shouldClearInstances(Class)[Command]

这将使立即查看“ Writer”数据集的测试用例(Writer.class)失败变得容易得多。

展开
收起
垚tutu 2019-12-19 16:57:18 1127 分享 版权
阿里云 AI 助理回答

在JUnit 5中,为了改善Gradle测试报告的可读性,你可以通过自定义DisplayNameGenerator来实现更友好的测试名称。这允许你控制如何生成显示给用户的测试用例名称。

首先,创建一个自定义的DisplayNameGenerator类,如下所示:

import org.junit.jupiter.api.DisplayNameGenerator;
import org.junit.jupiter.api.DisplayNameGenerator.Standard;

public class CustomDisplayNameGenerator extends Standard {

    @Override
    public String generateDisplayNameForClass(Class<?> testClass) {
        return super.generateDisplayNameForClass(testClass);
    }

    @Override
    public String generateDisplayNameForNestedClass(Class<?> nestedClass) {
        return super.generateDisplayNameForNestedClass(nestedClass);
    }

    @Override
    public String generateDisplayNameForMethod(Class<?> testClass, Method method, DisplayName annotation, int index) {
        String methodName = method.getName();
        if (method.getParameterCount() > 0 && method.getParameters()[0].getType() == Class.class) {
            // 假设参数是Class类型,尝试获取实际的类名作为标识
            try {
                Object[] args = getMethodArguments(method, index);
                if (args != null && args.length > 0 && args[0] instanceof Class) {
                    String className = ((Class<?>) args[0]).getSimpleName();
                    return methodName + " [" + className + "]";
                }
            } catch (Exception e) {
                // 处理可能的异常
            }
        }
        return super.generateDisplayNameForMethod(testClass, method, annotation, index);
    }

    private Object[] getMethodArguments(Method method, int index) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        // 这里简化处理,实际情况可能需要更复杂的逻辑来获取参数
        // 注意:此方法仅作示例,实际环境中可能无法直接工作
        throw new UnsupportedOperationException("获取方法参数的逻辑需根据具体环境实现");
    }
}

然后,在你的测试类或者整个项目中应用这个自定义的DisplayNameGenerator。如果你希望全局应用,可以在你的JUnit配置文件(通常是junit-platform.properties)中添加以下内容:

junit.jupiter.displayname.generator.default=your.package.name.CustomDisplayNameGenerator

如果只想在特定测试类中使用,可以在类级别使用注解:

@DisplayNameGenerator(CustomDisplayNameGenerator.class)
public class FactoredTest {
    // ... 测试代码
}

请注意,上述代码中的getMethodArguments方法是为了演示目的而简化的。实际上,JUnit框架并不直接提供从测试执行上下文中获取参数值的方法,因此你需要自己实现这部分逻辑,这可能涉及到与JUnit运行时环境的更深层次交互,而这通常不是直接支持或推荐的做法。

由于直接获取参数值在标准JUnit API中没有直接的支持,你可能需要考虑其他间接方式,比如利用测试生命周期回调、反射等技术手段,但这可能会引入额外的复杂性和维护成本。因此,尽管上述方案提供了理论上的解决思路,但在实践中可能需要权衡是否值得投入资源去实现这样的定制化功能。

有帮助
无帮助
AI 助理回答生成答案可能存在不准确,仅供参考
0 条回答
写回答
取消 提交回答