Enterprise Java

@ParameterizedTest with null values in @CvsSource

Writing parameterized tests in JUnit 4 was pretty cumbersome. JUnit 5 introduced several useful improvements to the framework and running the same test with different arguments is much simpler than in the previous version. However, there is one small issue with passing null values in such arguments.

In this post, I’m going to show you how to pass null in @CvsSource and @ValueSource for @ParametrziedTest in JUnit 5.

1. Null values in @CsvSource

In order to analyze the problem, we need a sample case.

Let’s say that we create a class called DateRange which contains two boundary dates of a time period. You can create a new object only if you pass at least one boundary date to the constructor. We also need to make sure the start date is before the end date.

With @ParametrizedTest from JUnit 5 we can describe given requirements with two following tests.

@ParameterizedTest
@CsvSource({
       "2017-06-01, 2018-10-15",
       "null, 2018-10-15",
       "2017-06-01, null"
})
void shouldCreateValidDateRange(LocalDate startDate, LocalDate endDate) {
   new DateRange(startDate, endDate);
}

@ParameterizedTest
@CsvSource({
       "2018-10-15, 2017-06-01",
       "null, null"
})
void shouldNotCreateInvalidDateRange(LocalDate startDate, LocalDate endDate) {
   assertThrows(IllegalArgumentException.class, () -> new DateRange(startDate, endDate));
}

However, when you try to execute these tests, you will end up with an error similar to the one presented below.

org.junit.jupiter.api.extension.ParameterResolutionException: Error converting parameter at index 0: Failed to convert String “null” to type java.time.LocalDate

Although JUnit 5 comes with numerous built-in converters from string values to different types, the null value isn’t accepted in @ValueSource or @CsvSource.

So how can you force JUnit 5 to work with null literals?

2. Custom nullable argument converter

Fortunately, JUnit 5 is flexible and we can easily extend its features.

By default, the framework uses the DefaultArgumentConverter class to convert Strings into other types. Our goal is to represent String “null” as the null literal. Other string values should be converted with the default converter.

In order to do so, we create a class which extends SimpleArgumentConverter and implement its abstract convert() method. In the body, we check for “null” values. In other cases, we execute the default converter.

import org.junit.jupiter.params.converter.DefaultArgumentConverter;

public final class NullableConverter extends SimpleArgumentConverter {
   @Override
   protected Object convert(Object source, Class<?> targetType) throws ArgumentConversionException {
       if ("null".equals(source)) {
           return null;
       }
       return DefaultArgumentConverter.INSTANCE.convert(source, targetType);
   }
}

Note that the signature of the DefaultArgumentConverter.convert() presented above is available since JUnit 5.2.

3. Using custom argument converter

Once our custom converter is ready, we can call it in our tests using the @ConvertWith annotation.

@ParameterizedTest
@CsvSource({
       "2017-06-01, 2018-10-15",
       "null, 2018-10-15",
       "2017-06-01, null"
})
void shouldCreateValidDateRange(@ConvertWith(NullableConverter.class) LocalDate startDate,
                                @ConvertWith(NullableConverter.class) LocalDate endDate) {
   new DateRange(startDate, endDate);
}

Conclusion

At this point, you should already know how to accept null values in JUnit 5 argument sources for parameterized tests. I hope such conversion will be automatic in future releases of the framework. For now, we need a small workaround.

In case of any question please leave it in the comments. If you want to know about the latest posts, follow me or join the subscription list.

Published on Java Code Geeks with permission by Daniel Olszewski, partner at our JCG program. See the original article here: @ParameterizedTest with null values in @CvsSource

Opinions expressed by Java Code Geeks contributors are their own.

Daniel Olszewski

Daniel Olszewski is a software developer passionate about the JVM ecosystem and web development. He is an advocate of clean, maintainable, and well tested code
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button