JUnit 5中的@MethodSource在嵌套类中

50

我正在使用junit5,并希望在嵌套类中创建参数化测试。例如:

class CardTest {

    @Nested
    class Cost {
        Stream<Arguments> cards() {
            return Stream.of(
                    Arguments.of(Card.common(0, Color.RED), 0),
                    /** Other Data **/
                    Arguments.of(Card.choseColor(), 50)
            );
        }

        @MethodSource("cards")
        @ParameterizedTest
        void cardCost(Card card, int cost) {
            assertThat(card.cost())
                    .isEqualTo(cost);
        }
    }
    /** Some other nested classes or simple methods **/
}

问题在于@MethodSource要求指定的方法必须是static。但是Java不允许在非静态内部类中使用静态方法。如果我创建一个名为Cost的静态类,那么它将不会被junit收集。

我该怎么解决这个问题?

4个回答

52

@TestInstance(PER_CLASS)

您可以使用“每个类单个测试实例”模式,对嵌套的类进行注释:@TestInstance(TestInstance.Lifecycle.PER_CLASS)

class ColorTest {

    @Nested
    @TestInstance(TestInstance.Lifecycle.PER_CLASS)
    class Inner {

        @ParameterizedTest
        @MethodSource("colors")
        void blue(Color color, int blue) {
            Assertions.assertEquals(color.getBlue(), blue);
        }

        Stream<Arguments> colors() {
            return Stream.of(
                    Arguments.of(Color.BLACK, 0),
                    Arguments.of(Color.GRAY, 128),
                    Arguments.of(Color.BLUE, 255)
            );
        }
    }

}
当使用此模式时,每个测试类将创建一个新的测试实例。
ArgumentsProvider 或者您可以从MethodSource切换到ArgumentsProvider。
我修改了你的示例以查看它是否可以在本地编译和运行。
class ColorTest {

    static class Blues implements ArgumentsProvider {

        @Override
        public Stream<Arguments> provideArguments(ExtensionContext context) {
            return Stream.of(
                    Arguments.of(Color.BLACK, 0),
                    Arguments.of(Color.GRAY, 128),
                    Arguments.of(Color.BLUE, 255)
            );
        }
    }

    @Nested
    class Inner {

        @ParameterizedTest
        @ArgumentsSource(Blues.class)
        void blue(Color color, int blue) {
            Assertions.assertEquals(color.getBlue(), blue);
        }
    }

}

更多细节请参见http://junit.org/junit5/docs/current/user-guide/#writing-tests-parameterized-tests


我认为在这种情况下,测试数据和测试是逻辑上分开的。我不确定这是否是一个好主意。 - eugene-karanda
3
添加了第二种解决方案,它允许非静态方法源,并将测试数据和逻辑放在原地。注意:使用此模式时,每个测试类将创建一个新的测试实例。 - Sormuras
Java让我烦恼的是它不允许我在非静态内部类中使用静态方法。这对我来说似乎有点武断。 - Hakanai
@Hakanai,从Java 16开始,非静态内部类中可以有静态方法。 - Valerij Dobler
@ValerijDobler 很好,也许我很快就能用到它。 - Hakanai

23

基于JUnit 5.2.0的另一种变体如下。

class ColorTest {

public static Stream<Arguments> provideColors() {
    return Stream.of(
            Arguments.of(Color.BLACK, 0),
            Arguments.of(Color.GRAY, 128),
            Arguments.of(Color.BLUE, 255)
    );
}

@Nested
class Inner {

    @ParameterizedTest
    @MethodSource("com.domain.ColorTest#provideColors")
    void blue(Color color, int blue) {
        Assertions.assertEquals(color.getBlue(), blue);
    }
}

1
它运行良好。请注意,即使方法provideColors是私有的(和静态的),它也能正常工作。 - user07

8

对于这个问题来说,有点晚了,但是...

你可以将provider作为静态类实现在外部类中。然后,在@MethodSource中,你只需要提供参数的完全限定名称(即com.biz.pckg#colors)。

JUnit用户指南中有详细说明。


2
是的,这回答了原始问题。更完整的示例在此处:https://dev59.com/aFQJ5IYBdhLWcg3wYU1O基本上,除了fqcn之外,您需要知道使用#而不是. - user2077221

0
这是关于Kotlin的解决方案:
class MyContainerTest {

    @Nested
    @TestInstance(Lifecycle.PER_CLASS)
    inner class MyNestedTest {
 
        @ParameterizedTest
        @MethodSource("generateArgs")
        fun `Test should work`(
            argument: String
        ) {
            // Use the argument
        }

        private fun generateArgs() = listOf(
            "abc",
            "xyz",
            "1234"
        )
    }
}

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