Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JUnit5 parameterized tests at class level

Is it possible to use JUnit5's parameterized new features to run test classes to receive test parameters instead of doing it at method level?

With JUnit 4 a runner such as @RunWith(Parameterized::class) plus inheritance could be used to pass an array of parameters to subclasses, but I am not sure if it is possible to achieve something equivalent but using the new JUnit 5 api.

like image 849
IS1_SO Avatar asked Sep 12 '17 17:09

IS1_SO


People also ask

How do you write parameterized test cases in junit5?

Writing Our First Parameterized TestsAdd a new test method to our test class and ensure that this method takes a String object as a method parameter. Configure the display name of the test method. Annotate the test method with the @ParameterizedTest annotation. This annotation identifies parameterized test methods.

How does JUnit parameterize a class?

JUnit 4 has introduced a new feature called parameterized tests. Parameterized tests allow a developer to run the same test over and over again using different values. There are five steps that you need to follow to create a parameterized test. Annotate test class with @RunWith(Parameterized.

What is parameterized test in JUnit 5?

Some times we may need to run same tests with different arguments or values, Junit 5 Jupiter Parameterized tests makes it possible to run a test multiple times with different arguments. They are declared just like regular @Test methods but use the @ParameterizedTest annotation instead of @Test annotation.

Does JUnit support parameterized tests?

JUnit 5, the next generation of JUnit, facilitates writing developer tests with shiny new features. One such feature is parameterized tests. This feature enables us to execute a single test method multiple times with different parameters.


2 Answers

Short answer
there's no way to parametrize class creation with JUnit 5 following the style of JUnit 4.

Fortunately, the very intention of separation test logic and test input data (parameters) can be implemented differently.


JUnit 5 has its own approach for making parameterized tests, and, of course, it is different from JUnit 4. The new approach does not allow to use parameterized fixtures at the class level, i.e. through its every test method. So every parameterized test method should be explicitly annotated with a link to parameters.

JUnit 5 provides a plenty of parameter source types, that could be found in documentation and guides

In your case, the simplest way to migrate from @Parameters of Junit 4 is using @MethodSource or @ArgumentsSource of org.junit.jupiter.params.provider.*.

JUnit 4:

@RunWith(Parameterized.class) public class MyTestWithJunit4 {     @Parameters     public static Collection<Object[]> data() {       return Arrays.asList(new Object[][] {                     { 0, 0, 0 },                { 1, 2, 3 },                 { 5, 3, 8 }        });     }      int first;     int second;     int sum;      public MyTestWithJunit4(int first, int second, int sum) {       this.first = first;       this.second = second;       this.sum = sum;     }      @Test     public void test() {       assertEquals(sum, first + second));     } } 

JUnit 5 (with @MethodSource):

class MyTestWithJunit5 {    @DisplayName("Test with @MethodSource")   @ParameterizedTest(name = "{index}: ({0} + {1}) => {2})")   @MethodSource("localParameters")   void test(int first, int second, int sum) {     assertEquals(sum, first + second);   }    static Stream<Arguments> localParameters() {     return Stream.of(         Arguments.of(0, 0, 0),         Arguments.of(1, 2, 3),         Arguments.of(5, 3, 8)     );   } } 

JUnit 5 (with @ArgumentsSource):

class MyTestWithJunit5 {   @DisplayName("Test with @ArgumentsSource")   @ParameterizedTest(name = "{index}: ({0} + {1}) => {2})")   @ArgumentsSource(Params.class)   void test(int first, int second, int sum) {     assertEquals(sum, first + second);   }    static class Params implements ArgumentsProvider {     @Override     public Stream<? extends Arguments> provideArguments(ExtensionContext context) {       return Stream.of(           Arguments.of(0, 0, 0),           Arguments.of(1, 2, 3),           Arguments.of(5, 3, 8)       );     }   } } 

Consider that a method in @MethodSource and a class in @ArgumentsSource could be described anywhere, not only inside the same class where the test methods are located. Also @MethodSource allows to provide multiple source methods, since its value is a String[].

Some remarks and comparison

In JUnit 4 we could only have a single factory method providing parameters, and the tests were supposed to be built around those parameters. On the contrary, JUnit 5 gives more abstraction and flexibility in binding parameters and decouples test logic from its parameters, which are secondary. That allows building tests independently from parameter sources, and easily change them when needed.

Dependency requirement

Parameterized tests feature is not included in the core junit-jupiter-engine, but is located in a separate dependency junit-jupiter-params.

like image 68
diziaq Avatar answered Sep 25 '22 04:09

diziaq


Create a meta annotation that specifies the parameterisation, and apply it to the test methods:

public class MyTest {
    @ParameterizedTest(name = "{0}")
    @MethodSource("allImplementations")
    @Retention(RetentionPolicy.RUNTIME)
    private @interface TestAllImplementations {
    }

    static Stream<Arguments> allImplementations() {
        return Stream.of(
             Arguments.of("Serial", new SerialWidget()),
             Arguments.of("Concurrent", new ConcurrentWidget())
        );
    }

    @TestAllImplementations
    void myFirstTest(String name, Widget implementation) {
        /* ... */
    }

    @TestAllImplementations
    void mySecondTest(String name, Widget implementation) {
        /* ... */
    }
}
like image 44
hertzsprung Avatar answered Sep 26 '22 04:09

hertzsprung