JUnit 5 parameterization test is super convenient

There was a time when I thought that the @Nested test was the best choice when it came to JUnit 5, but when I started using @ParameterizedTest, I got used to it so much that I thought "What was that in JUnit 4?" feel well. This alone can recommend migration.

Confirmation environment

ValueSource

Parameters are specified using the @ValueSource annotation. Depending on the parameter type, there are ʻints, strings, and doubles` properties.

@ParameterizedTest
@ValueSource(ints = {1, 2, 100})
void positiveNumber(int n) {
    assertTrue(isPositiveNumber(n));
}
@ParameterizedTest
@ValueSource(strings = {"Java", "java", "JAVA"})
void upperCase(String s) {
    assertEquals("JAVA", s.toUpperCase());
}

When run in the IDE, the test results for each parameter are fed back in an easy-to-understand manner. IntelliJ IDEA also allows you to rerun the test for certain parameters.

image.png

Implicit type conversion

The parameters that can be specified with @ValueSource are limited to primitive systems, but implicit type conversion from String is supported for other commonly used types. This is insanely useful if you want to write a date test.

@ParameterizedTest
@ValueSource(strings = {"2016-01-01", "2020-01-01", "2020-12-31"})
void leapYear(LocalDate date) {
    assertTrue(date.isLeapYear());
}

For more information on supported type conversions, see the Official Documentation (https://junit.org/junit5/docs/5.3.0/user-guide/#writing-tests-parameterized-tests-argument-conversion-implicit). please refer to.

MethodSource

While @ValueSource can only give one parameter at a time, using @MethodSource allows you to:

By giving the expected value as a parameter, you can easily write Table Driven Test as recommended by Go language. This is especially useful if you want to write tests for utility methods that just return a String or boolean.

@ParameterizedTest
@MethodSource("japaneseDateProvider")
void japaneseEra(JapaneseDate date, String expected) {
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("G").withLocale(Locale.JAPAN);
    assertEquals(expected, formatter.format(date));
}

static Stream<Arguments> japaneseDateProvider() {
    return Stream.of(
        arguments(JapaneseDate.of(1989, 1, 7), "Showa"),
        arguments(JapaneseDate.of(1989, 1, 8), "Heisei"),
        arguments(JapaneseDate.of(2019, 4, 30), "Heisei"),
        arguments(JapaneseDate.of(2019, 5, 1), "Reiwa")
    );
}

CsvSource

@MethodSource has to define different methods one by one, and there is a little noise other than test data. If you don't need your own type, you can use @CsvSource to work around this issue.

Each field separated by a comma is a parameter. The Implicit Type Conversion (#Implicit Type Conversion) mechanism described earlier automatically maps a String to a method argument type.

@ParameterizedTest
@CsvSource({
    "1989-01-07,Showa",
    "1989-01-08,Heisei",
    "2019-04-30,Heisei",
    "2019-05-01,Reiwa"
})
void japaneseEra(LocalDate date, String expected) {
    JapaneseDate d = JapaneseDate.from(date);
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("G").withLocale(Locale.JAPAN);
    assertEquals(expected, formatter.format(d));
}

Since enums are also subject to implicit type conversion, you can write code like this: It's easy and very convenient, but I think it's easy to forget to modify the scope of influence when refactoring the name of an enum constant name. I can't say anything because I haven't been to that scene yet.

@ParameterizedTest
@CsvSource({
    "JANUARY, false, 31",
    "FEBRUARY, false, 28",
    "FEBRUARY, true, 29"
})
void daysOfMonth(Month month, boolean leapYear, int expected) {
    assertEquals(expected, month.length(leapYear));
}

If you want to include an empty string, enclose it in single quotes like " 2,'', true ". On the other hand, if you do " 2,, true " without single quotes, it will be null, so be careful.

Summary

We have briefly introduced the parameterized tests that have been revamped in JUnit 5. The parameterization test of JUnit 4 was difficult to understand, and there were many scenes where I wrote a loop to avoid it, but I recommend this because it is very intuitive to use in JUnit 5.

References

Recommended Posts

JUnit 5 parameterization test is super convenient
try-with-resources is convenient
Try JUnit test launch
[JUnit 5] Write a validation test with Spring Boot! [Parameterization test]
Unit test with Junit.
[Java] JUnit4 test case example
Test Web API with junit
Test private methods in JUnit
Test private methods in JUnit
[JUnit] Test the thrown exception
ProxyFactory is convenient when you want to test AOP in Spring!
[Spring Boot] Until @Autowired is run in the test class [JUnit5]