JUnit 5中@ParameterizedTest的显示名称生成

44
我有一堆使用@MethodSource接收参数的@ParameterizedTest,这些参数在toString()中返回冗长的结果(例如Selenium的WebDriver)。默认情况下,这些用于组成相应的显示名称。根据JUnit 5用户指南

默认情况下,参数化测试调用的显示名称包含调用索引和该特定调用的所有参数的字符串表示形式。但是,您可以通过@ParameterizedTest注释的name属性自定义调用显示名称[...]。

尽管这允许以一定程度上自定义显示名称,但似乎我无法适应单个参数的字符串表示形式。不幸的是,通过@DisplayNameGeneration指定生成器只能应用在类级别,并且不影响参数化测试调用的显示名称。有没有办法在@ParameterizedTest中使用DisplayNameGenerator,或者自定义给定参数的字符串表示形式?
4个回答

75

截至JUnit 5.8.0,JUnit Jupiter API中有一个Named<T>接口,具有“自动支持将所包含的有效负载[参数]直接注入参数化方法”的功能(请参见问题#2301 )。 示例:

@DisplayName("A parameterized test with named arguments")
@ParameterizedTest
@MethodSource("namedArguments")
void testWithNamedArguments(File file) {}

static Stream<Arguments> namedArguments() {
    return Stream.of(
        Arguments.of(Named.of("An important file", new File("path1"))),
        Arguments.of(Named.of("Another file", new File("path2")))
    );
}

如果您喜欢静态导入,您也可以选择来自ArgumentsNamed的相应别名:

arguments(named("An important file", new File("path1")))

更多信息,请参考相关文档


"Named.of()" 是自定义显示名称的好方法。比添加一个不会被使用的额外参数更加简洁。 - armandino
1
(name = "{index}: {0}") can be skipped when using Named - Thánh Ma
@ThánhMa 谢谢你,我相应地更新了答案。输出略有不同(例如,“[1] 一个重要文件”而不是“1:一个重要文件”),但没有信息丢失。 - beatngu13
1
对于多个输入,请使用Stream.of(arguments(named("cool name", "first input"), "second input"))@ParameterizedTest(name = "{0}") - Domadin
@Domadin 这可能是我见过的最丑陋的API设计了。 - undefined

39
为了间接地实现你的目标,直到JUnit直接支持它,我们可以在测试中添加一个参数作为所需的字符串。然后,自定义需要在参数提供者方法中完成。
@ParameterizedTest(name = "{index} {1}")
@MethodSource("argumentProvider")
public void testSomething(Object someObject, String niceRepresentation) {
    // Test something
}

private static Stream<Arguments> argumentProvider() {
    return Stream.of(
            Arguments.of(new Object(), "Nice 1"),
            Arguments.of(new Object(), "Nice 2")
    );
}

缺点是单元测试会提供在测试实现中未使用的参数,这可能会降低清晰度,但随之而来的是在测试报告中使用冗长的名称需要进行权衡。

我已经考虑过类似的事情了。一个方法是扩展参数类型并覆盖 toString(),但我希望有更好的解决方案。尽管如此,这也是一个有效的解决方案 - 谢谢! - beatngu13
1
这不是类型安全的,因为实际参数可能会改变,导致字符串不匹配,但现在似乎是唯一的方法。 - Abhijit Sarkar

2

我遇到了一个和surefire报告相关的问题。

在使用@DisplayName@ParameterizedTest(name = "{0}")之后,报告中没有显示正确的测试名称。

这个问题通过#1255得到了解决。

       <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-surefire-plugin</artifactId>
            <version>3.0.0-M4</version>
            <configuration>
                <statelessTestsetReporter
                        implementation="org.apache.maven.plugin.surefire.extensions.junit5.JUnit5Xml30StatelessReporter">
                    <disable>false</disable>
                    <version>3.0</version>
                    <usePhrasedFileName>false</usePhrasedFileName>
                    <usePhrasedTestSuiteClassName>true</usePhrasedTestSuiteClassName>
                    <usePhrasedTestCaseClassName>true</usePhrasedTestCaseClassName>
                    <usePhrasedTestCaseMethodName>true</usePhrasedTestCaseMethodName>
                </statelessTestsetReporter>
            </configuration>
        </plugin>

0
对于更大的单元(例如需要更多参数,与几个服务交互等),我通常使用TestCase记录,并覆盖toString()方法以获得一个漂亮的显示名称。
class Test {
    
    private record TestCase(
        String testName,
        // params, expected outputs, validators, etc
    ) {
        @Override
        public String toString() {
            return testName;
        }
    }

    @ParameterizedTest(name = "{index}. {0}")
    @MethodSource("testCases_source")
    void checkTestCase(TestCase testCase) {
        // test method impl
    }

    public static Stream<Arguments> testCases_source() {
        return TestCases.testCasesStream(
            new TestCase(
                "should return value and invoke the method", // testName
                InputDto.of("v1"), // input 
                (service) -> { verify(mockk, times(1)).method() }, // verifier
                true // output       
            )
            new TestCase(
                "should return value and invoke the service twice",
                // ...
            )
            // other test cases 
        )
    }

    // helper from other class to apply some syntax sugar:
    public Stream<Arguments> testCasesStream(TestCase... testCases) {
        return Arrays.stream(testCases).map(Arguments::of); 
    }
}

不幸的是,Java 没有任何记录的创建模式,因此通常我会用 Java Class + Lombok(或者使用 Kotlin)来替换记录。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接