`@EmptySource` not working with `ArgumentConverter` accepting its supported types
Currently, EmptyArgumentsProvider directly checks the type of the first declared parameter and does not know of a potential converter declared for that parameter with its supported type(s), so it will throw a PreconditionViolationException if the declared type doesn't match the expectations.
Looking ahead to when #4219 is integrated, a globally-configured converter might even make the situation more complicated.
Steps to reproduce
Given an ArgumentConverter converting objects to their respective hash codes, like:
class ObjectToHashCodeConverter implements ArgumentConverter {
@Override
public Object convert(Object source, ParameterContext context) {
return source.hashCode();
}
}
the following test passes:
@ParameterizedTest
@FieldSource
void fieldSourceTest(@ConvertWith(ObjectToHashCodeConverter.class) int hashCode) {
assertEquals(0, hashCode);
}
static List<?> fieldSourceTest = List.of("");
but the following test does not:
@ParameterizedTest
@EmptySource // no indication on which supported type to use
void emptySourceTest(@ConvertWith(ObjectToHashCodeConverter.class) int hashCode) {
assertEquals(0, hashCode);
}
failing with:
org.junit.platform.commons.PreconditionViolationException: @EmptySource cannot provide an empty argument to method [void io.github.scordio.junit.converters.tests.MyTest.emptySourceTest(int)]: [int] is not a supported type.
at [email protected]/org.junit.jupiter.params.provider.EmptyArgumentsProvider.provideArguments(EmptyArgumentsProvider.java:92)
at [email protected]/org.junit.jupiter.params.ParameterizedInvocationContextProvider.arguments(ParameterizedInvocationContextProvider.java:79)
at [email protected]/org.junit.jupiter.params.ParameterizedInvocationContextProvider.lambda$provideInvocationContexts$2(ParameterizedInvocationContextProvider.java:46)
at java.base/java.util.stream.ReferencePipeline$7$1.accept(ReferencePipeline.java:273)
at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1708)
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509)
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:151)
at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:174)
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:596)
at [email protected]/org.junit.jupiter.engine.descriptor.TemplateExecutor.executeForProvider(TemplateExecutor.java:59)
...
Similar reproducers can be composed for all other @EmptySource supported types, like Collection, Map, etc.
Context
- Used versions (Jupiter/Vintage/Platform): 5.13.4
- Build Tool/IDE: Maven/IntelliJ IDEA
Deliverables
- [ ]
@EmptySourceprovides an optionaltypeattribute to select the desired type explicitly
The more I think about it, the more I feel like a solution might require a level of complexity that doesn't justify the gain...
I mentioned as a deliverable:
@EmptySourceprovides the empty argument accepted by the available converter
What if the available converter accepts more than one type supported by @EmptySource, e.g., both Collection and Map? Which empty instance should @EmptySource provide?
If StringToBytesConverter would just implement ArgumentConverter instead of TypedArgumentConverter, it could allow byte[] to pass through, right?
Another idea would be to add a type attribute to @EmptySource which it would use instead of inspecting the method parameter type:
@ParameterizedTest
@EmptySource(type = String.class)
void emptySourceTest(@ConvertWith(StringToBytesConverter.class) byte[] bytes) {
assertEquals(0, bytes.length);
}
If
StringToBytesConverterwould just implementArgumentConverterinstead ofTypedArgumentConverter, it could allowbyte[]to pass through, right?
First and foremost, I apologize for mixing up two things.
I mentioned:
Currently,
EmptyArgumentsProvider[...] will throw aPreconditionViolationExceptionif the declared type doesn't match the expectations.
but then I reported:
org.junit.jupiter.api.extension.ParameterResolutionException
which is an unfortunate coincidence of my simplified reproducer... the fact that @EmptySource supported the converter's target type was not intended 😅 and I haven't added any stack trace, which would have helped to spot my mistake...
Therefore, I updated the issue description with a more specific example and the missing stack trace.
Another idea would be to add a
typeattribute to@EmptySourcewhich it would use instead of inspecting the method parameter type
Yes, that would help! I updated the deliverables accordingly.
If the change is agreed, I'll be happy to work on it.
If the change is agreed, I'll be happy to work on it.
I've added it to our discussion backlog which is currently very long so I'm afraid it may be a while before we get back to you on that.