50

I am working with junit5 and I want to create Parameterized Test in nested class. For example:

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 **/
}

The problem is @MethodSource required that specified method must be static. But Java not allowed static method in non-static inner classes. If I create class Cost static then it won't be collected by junit.

What should I do to resolve this issue?

Ele
  • 33,468
  • 7
  • 37
  • 75
eugene-karanda
  • 760
  • 1
  • 6
  • 9

3 Answers3

52

@TestInstance(PER_CLASS)

You may select the "single test instance per class" mode annotating the nested class with @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)
            );
        }
    }

}

When using this mode, a new test instance will be created once per test class.

ArgumentsProvider

Or you may switch from a MethodSource to an ArgumentsProvider.

I modified your example to see if it compiles and runs locally:

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);
        }
    }

}

More details at http://junit.org/junit5/docs/current/user-guide/#writing-tests-parameterized-tests

Sormuras
  • 8,491
  • 1
  • 38
  • 64
  • I think in this case test data and the test are logically spitted. I don't sure that is a good idea. – eugene-karanda Dec 21 '17 at 23:19
  • 3
    Added second solution that a) allows a non-static method source and b) ties the test data and logic in-place. Caveat: when using this mode, a new test instance will be created once per test class. – Sormuras Dec 22 '17 at 16:05
  • It annoys me that Java won't just let me have a static method in my inner non-static class. Seems like a bit of an arbitrary decision to me. – Hakanai Jun 18 '21 at 06:24
  • @Hakanai as of Java 16 it is possible to have static methods in non-static inner classes. – Valerij Dobler Oct 28 '22 at 06:52
  • @ValerijDobler nice, maybe I'll get to use that some day soon. – Hakanai Oct 29 '22 at 05:43
23

Another variation based on JUnit 5.2.0 is this.

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);
    }
}

}

Mohan Radhakrishnan
  • 3,002
  • 5
  • 28
  • 42
  • 1
    It works well. To note that it will work even if the method provideColors will be private (and static). – user07 Apr 12 '23 at 19:07
8

A bit late to this game, but...

You can implement the provider, as a static, in the outer class. Then, in @MethodSource, you simply need to provide the fully qualified name of the parameter (i.e., com.biz.pckg#colors).

This is documented in the JUnit user guide.

jpenna
  • 8,426
  • 5
  • 28
  • 36
SoCal
  • 801
  • 1
  • 10
  • 23
  • 2
    Yes, this answers the original question. A more complete example is here: https://stackoverflow.com/questions/53975605/how-to-use-methodsource-defined-in-other-class-in-junit-5 Basically, in addition to the fqcn, you need to know that you use # not . – user2077221 Jun 18 '21 at 21:14