Issue
I have been following some online course on JUnit testing and I came across an example of @CsvSource
. Following example works very well.
@DisplayName("CSV input test")
@ParameterizedTest(name = DISPLAY_NAME_PLACEHOLDER + " - [" + INDEX_PLACEHOLDER + "] " + ARGUMENTS_PLACEHOLDER)
@CsvSource({
"FL, 1, 1",
"OH, 2, 2",
"MI, 3, 3"
})
void csvInputTest(String stateCode, int val1, int val2) {
System.out.println(stateCode + " - " + val1 + " - " + val2);
}
And I started wondering whether I could use some POJO instead of bazilion of arguments. So I created the POJO class with setters and getters and changed my method to:
void csvInputTest(StateInfo stateInfo) {
System.out.println(stateInfo.getStateCode() + " - " + stateInfo.getVal1() + " - " + stateInfo.getVal2());
}
but this resulted into:
org.junit.jupiter.api.extension.ParameterResolutionException: Error converting parameter at index 0: No implicit conversion to convert object of type java.lang.String to type StateInfo
I couldn't find any fields in @CsvSource
to specify any converter so my question is - am I able to do so? Or do I have to stick to N-arguments
approach?
Solution
The official documentation explains it :
To use a custom aggregator, implement the ArgumentsAggregator interface and register it via the @AggregateWith annotation on a compatible parameter in the @ParameterizedTest method. The result of the aggregation will then be provided as an argument for the corresponding parameter when the parameterized test is invoked. Note that an implementation of ArgumentsAggregator must be declared as either a top-level class or as a static nested class.
It would give :
public class StateInfoAggregator implements ArgumentsAggregator {
@Override
public StateInfo aggregateArguments(ArgumentsAccessor arguments, ParameterContext context) {
return new StateInfo(arguments.getString(0),
arguments.getInteger(1),
arguments.getInteger(1));
}
}
@ParameterizedTest
@CsvSource({
"FL, 1, 1",
"OH, 2, 2",
"MI, 3, 3"
})
void csvInputTest(@AggregateWith(StateInfoAggregator.class) StateInfo stateInfo) {
System.out.println(stateInfo.getStateCode() + " - " + stateInfo.getVal1() + " - " + stateInfo.getVal2());
}
The documentation also adds :
If you find yourself repeatedly declaring @AggregateWith(MyTypeAggregator.class) for multiple parameterized test methods across your codebase, you may wish to create a custom composed annotation such as @CsvToMyType that is meta-annotated with @AggregateWith(MyTypeAggregator.class).
It would give :
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
@AggregateWith(StateInfoAggregator.class)
public @interface CsvToStateInfo {
}
@ParameterizedTest
@CsvSource({
"FL, 1, 1",
"OH, 2, 2",
"MI, 3, 3"
})
void csvInputTest(@CsvToStateInfo StateInfo stateInfo) {
System.out.println(stateInfo.getStateCode() + " - " + stateInfo.getVal1() + " - " + stateInfo.getVal2());
}
Answered By - davidxxx